UNPKG

@awayfl/avm1

Version:

Virtual machine for executing AS1 and AS2 code

174 lines (156 loc) 4.7 kB
/* * Copyright 2014 Mozilla Foundation * * 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. */ export class ActionsDataStream { private array: Uint8Array; public position: number; public end: number; private readANSI: boolean; constructor(array, swfVersion) { this.array = array; this.position = 0; this.end = array.length; // TODO use system locale to determine if the shift-JIS // decoding is necessary this.readANSI = swfVersion < 6; // endianess sanity check const buffer = new ArrayBuffer(4); (new Int32Array(buffer))[0] = 1; if (!(new Uint8Array(buffer))[0]) { throw new Error('big-endian platform'); } } readUI8(): number { return this.array[this.position++]; } readUI16(): number { const position = this.position, array = this.array; const value = (array[position + 1] << 8) | array[position]; this.position = position + 2; return value; } readSI16(): number { const position = this.position, array = this.array; const value = (array[position + 1] << 8) | array[position]; this.position = position + 2; return value < 0x8000 ? value : (value - 0x10000); } readInteger(): number { const position = this.position, array = this.array; const value = array[position] | (array[position + 1] << 8) | (array[position + 2] << 16) | (array[position + 3] << 24); this.position = position + 4; return value; } readFloat(): number { const position = this.position; const array = this.array; const buffer = new ArrayBuffer(4); const bytes = new Uint8Array(buffer); bytes[0] = array[position]; bytes[1] = array[position + 1]; bytes[2] = array[position + 2]; bytes[3] = array[position + 3]; this.position = position + 4; return (new Float32Array(buffer))[0]; } readDouble(): number { const position = this.position; const array = this.array; const buffer = new ArrayBuffer(8); const bytes = new Uint8Array(buffer); bytes[4] = array[position]; bytes[5] = array[position + 1]; bytes[6] = array[position + 2]; bytes[7] = array[position + 3]; bytes[0] = array[position + 4]; bytes[1] = array[position + 5]; bytes[2] = array[position + 6]; bytes[3] = array[position + 7]; this.position = position + 8; return (new Float64Array(buffer))[0]; } readBoolean(): boolean { return !!this.readUI8(); } readANSIString(): string { let value = ''; let ch; while ((ch = this.readUI8())) { value += String.fromCharCode(ch); } return value; } readUTF8String(): string { let value = ''; let ch; while ((ch = this.readUI8())) { if (ch < 0x80) { value += String.fromCharCode(ch); continue; } if ((ch & 0xC0) === 0x80) { // Invalid UTF8 encoding: initial char -- using it as is value += String.fromCharCode(ch); continue; } const lastPosition = this.position - 1; // in case if we need to recover let currentPrefix = 0xC0; let validBits = 5; do { const mask = (currentPrefix >> 1) | 0x80; if ((ch & mask) === currentPrefix) { break; } currentPrefix = mask; --validBits; } while (validBits >= 0); let code = (ch & ((1 << validBits) - 1)); for (let i = 5; i >= validBits; --i) { ch = this.readUI8(); if ((ch & 0xC0) !== 0x80) { // Invalid UTF8 encoding: bad chars in the tail, using previous chars as is const skipToPosition = this.position - 1; this.position = lastPosition; while (this.position < skipToPosition) { value += String.fromCharCode(this.readUI8()); } continue; } code = (code << 6) | (ch & 0x3F); } if (code >= 0x10000) { value += String.fromCharCode((((code - 0x10000) >> 10) & 0x3FF) | 0xD800, (code & 0x3FF) | 0xDC00); } else { value += String.fromCharCode(code); } } return value; } readString(): string { return this.readANSI ? this.readANSIString() : this.readUTF8String(); } readBytes(length): Uint8Array { const position = this.position; const remaining = Math.max(this.end - position, 0); if (remaining < length) { length = remaining; } const subarray = this.array.subarray(position, position + length); this.position = position + length; return subarray; } }