├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── copyVersionToIndex.js ├── index.ts ├── lib ├── AVMEvent.ts ├── AVMStage.ts ├── AVMTestHandler.ts ├── IAVMHandler.ts ├── IAVMStage.ts ├── factories │ ├── as3webFlash │ │ └── display │ │ │ ├── StageAlign.ts │ │ │ └── StageScaleMode.ts │ ├── base │ │ ├── AVMVersion.ts │ │ ├── SWFTags.ts │ │ ├── binaryFileReader.ts │ │ ├── external.ts │ │ ├── flashlog.ts │ │ ├── lzma.ts │ │ ├── metrics.ts │ │ ├── options.ts │ │ ├── remoting.ts │ │ ├── settings.ts │ │ ├── utilities.ts │ │ └── utilities │ │ │ ├── ArrayUtilities.ts │ │ │ ├── Cache.ts │ │ │ ├── ClipboardService.ts │ │ │ ├── ColorUtilities.ts │ │ │ ├── Debug.ts │ │ │ ├── ExternalInterfaceService.ts │ │ │ ├── FileLoadingService.ts │ │ │ ├── FunctionUtilities.ts │ │ │ ├── HashUtilities.ts │ │ │ ├── IntegerUtilities.ts │ │ │ ├── LocalConnectionService.ts │ │ │ ├── NumberUtilities.ts │ │ │ ├── ObjectUtilities.ts │ │ │ ├── PromiseWrapper.ts │ │ │ ├── Shumway.ts │ │ │ ├── StringUtilities.ts │ │ │ ├── SystemResourcesLoadingService.ts │ │ │ ├── Telemetry.ts │ │ │ ├── UI.ts │ │ │ └── jsGlobal.ts │ └── timelinesounds │ │ ├── MovieClipSoundStream.ts │ │ ├── MovieClipSoundsManager.ts │ │ └── mp3decodersession.ts ├── parsers │ ├── CompressionMethod.ts │ ├── EagerlyParsedDictionaryEntry.ts │ ├── FlashWaveAudioParser.ts │ ├── ISymbol.ts │ ├── SWFFile.ts │ ├── SWFFrame.ts │ ├── SWFParser.ts │ ├── SymbolDecoder.ts │ └── utils │ │ ├── parser │ │ ├── OpenTypeParser.ts │ │ ├── SWFLowLevel.ts │ │ ├── bitmap.ts │ │ ├── button.ts │ │ ├── font.ts │ │ ├── image.ts │ │ ├── label.ts │ │ ├── shape.ts │ │ ├── sound.ts │ │ └── text.ts │ │ └── stream.ts ├── redirectResolver.ts └── stat │ └── Stat.ts ├── package.json ├── rollup.config.js ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = false 8 | insert_final_newline = false 9 | max_line_length = 120 10 | quote_type = single 11 | spaces_around_brackets = inside -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:@typescript-eslint/eslint-recommended" 9 | ], 10 | "globals": { 11 | "Atomics": "readonly", 12 | "SharedArrayBuffer": "readonly" 13 | }, 14 | "parser": "@typescript-eslint/parser", 15 | "parserOptions": { 16 | "ecmaVersion": 2018, 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "@typescript-eslint" 21 | ], 22 | "rules": { 23 | "array-bracket-spacing": "error", 24 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 25 | "indent": "off", 26 | "@typescript-eslint/indent": [ 27 | "error", 28 | "tab" 29 | ], 30 | "keyword-spacing": "error", 31 | "lines-between-class-members": [ 32 | "error", 33 | "always", { 34 | "exceptAfterSingleLine": true 35 | } 36 | ], 37 | // "@typescript-eslint/member-ordering": [ 38 | // "error", 39 | // { 40 | // "default": { 41 | // "memberTypes": [ 42 | // "public-static-field", 43 | // "protected-static-field", 44 | // "private-static-field", 45 | // "public-instance-field", 46 | // "protected-instance-field", 47 | // "private-instance-field", 48 | // "constructor", 49 | // "public-static-method", 50 | // "protected-static-method", 51 | // "private-static-method", 52 | // "public-instance-method", 53 | // "protected-instance-method", 54 | // "private-instance-method" 55 | // ], 56 | // "order": "alphabetically" 57 | // } 58 | // } 59 | // ], 60 | "no-unused-vars": "off", 61 | "@typescript-eslint/no-unused-vars": [ 62 | "error", 63 | { 64 | "args": "none" 65 | } 66 | ], 67 | "no-prototype-builtins": "off", 68 | "no-trailing-spaces": "error", 69 | "object-curly-spacing": ["error", "always"], 70 | "quotes": ["error", "single"], 71 | "semi": "error", 72 | "no-var": "warn", 73 | "prefer-const": "warn", 74 | "prefer-rest-params": "warn", 75 | "prefer-spread": "warn", 76 | "max-len": ["warn", { "code": 120 }], 77 | "space-before-blocks": "error", 78 | "space-in-parens": "error", 79 | "space-infix-ops": [ 80 | "error", 81 | { 82 | "int32Hint": true 83 | } 84 | ], 85 | "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], 86 | "space-unary-ops": "error", 87 | "@typescript-eslint/type-annotation-spacing": [ 88 | "error", 89 | { 90 | "before": false, 91 | "after": true, 92 | "overrides": { 93 | "arrow": { 94 | "before": true, 95 | "after": true 96 | } 97 | } 98 | } 99 | ] 100 | } 101 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: rob-bateman 7 | 8 | --- 9 | 10 | **Description** 11 | A clear and concise description of what the bug is. 12 | 13 | **Platform** 14 | - OS: [e.g. iOS] 15 | - Browser [e.g. chrome, safari] 16 | - Version [e.g. 22] 17 | 18 | **Steps to reproduce the issue** 19 | Steps to reproduce the behavior: 20 | 1. Go to '...' 21 | 2. Click on '....' 22 | 3. Scroll down to '....' 23 | 4. See error 24 | 25 | **Expected outcome** 26 | A clear and concise description of what you expected to happen, including any console logs, screenshots etc. 27 | 28 | **Actual outcome** 29 | A clear and concise description of what you actually see, including any traces, screenshots etc. 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | node_modules/ 3 | dist/ 4 | bundle/ 5 | docs/bin/ 6 | 7 | # Dev files 8 | npm-debug.log 9 | 10 | # Project property files 11 | .actionScriptProperties 12 | .flexLibProperties 13 | .settings/ 14 | .idea/ 15 | .project 16 | .DS_Store 17 | *.iml 18 | 19 | # Misc 20 | deploy_key 21 | deploy_key.pub -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | node_modules/ 3 | 4 | # Dev files 5 | npm-debug.log 6 | tests/ 7 | docs/ 8 | 9 | # Project property files 10 | .actionScriptProperties 11 | .flexLibProperties 12 | .settings/ 13 | .idea/ 14 | .project 15 | .DS_Store 16 | *.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swf-loader -------------------------------------------------------------------------------- /copyVersionToIndex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | var fs = require("fs"); 4 | 5 | 6 | // read in the /index.ts 7 | 8 | // use regex to find a console log for printing the version and update it for the new version 9 | 10 | // update /index.ts with the new content 11 | 12 | console.log("update src/index.ts with version:", process.env.npm_package_version); 13 | 14 | fs.readFile("./index.ts", 'utf8', function(err, data) { 15 | if (err) throw err; 16 | var re = /(.*[a-zA-Z]\s\-\s)(.*)(\"\)\;.*)/; 17 | //console.log("before", data) 18 | data = data.replace(re, "$1"+process.env.npm_package_version+"$3");//#BUILD_VIA_NPM_VERSION_PATCH_TO_DISPLAY_VERSION_HERE#", process.env.npm_package_version); 19 | //console.log("after", data) 20 | fs.writeFile("./index.ts", data, function(err) { 21 | if (err) throw err; 22 | console.log("Updated ./index.ts with inserted version ", process.env.npm_package_version); 23 | }); 24 | }); -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | console.debug("AwayFL - SWF-Loader - 0.4.131"); 2 | 3 | export { SWFFile } from "./lib/parsers/SWFFile"; 4 | 5 | export { AVMVERSION } from "./lib/factories/base/AVMVersion"; 6 | 7 | export { AVMEvent } from "./lib/AVMEvent"; 8 | export { AVMStage } from "./lib/AVMStage"; 9 | export { IAVMHandler } from "./lib/IAVMHandler"; 10 | export { IAVMStage } from "./lib/IAVMStage"; 11 | 12 | export { 13 | IRedirectRule, 14 | globalRedirectRules, 15 | matchRedirect, 16 | } from "./lib/redirectResolver"; 17 | export { SWFParser } from "./lib/parsers/SWFParser"; 18 | 19 | export { OpenTypeParser } from "./lib/parsers/utils/parser/OpenTypeParser"; 20 | 21 | export { 22 | Debug, 23 | release, 24 | notImplemented, 25 | somewhatImplemented, 26 | warning, 27 | assert, 28 | assertUnreachable, 29 | unexpected, 30 | assertNotImplemented, 31 | registerDebugMethod, // global _AWAY_DEBUG insterface 32 | IDebugMethodDeclaration, 33 | } from "./lib/factories/base/utilities/Debug"; 34 | 35 | export { shumwayOptions } from "./lib/factories/base/settings"; 36 | export { Option, OptionSet } from "./lib/factories/base/options"; 37 | 38 | export { StageScaleMode } from "./lib/factories/as3webFlash/display/StageScaleMode"; 39 | export { StageAlign } from "./lib/factories/as3webFlash/display/StageAlign"; 40 | export { MovieClipSoundsManager } from "./lib/factories/timelinesounds/MovieClipSoundsManager"; 41 | export { MovieClipSoundStream } from "./lib/factories/timelinesounds/MovieClipSoundStream"; 42 | 43 | export { 44 | isIndex, 45 | isNullOrUndefined, 46 | PromiseWrapper, 47 | isNumeric, 48 | isNumber, 49 | isString, 50 | toNumber, 51 | isObject, 52 | IndentingWriter, 53 | dumpLine, 54 | Bounds, 55 | } from "./lib/factories/base/utilities"; 56 | export { jsGlobal } from "./lib/factories/base/utilities/jsGlobal"; 57 | 58 | export { 59 | AVM1ClipEvents, 60 | ColorMatrixFilter, 61 | ConvolutionFilter, 62 | GlowFilter, 63 | BlurFilter, 64 | FilterType, 65 | } from "./lib/factories/base/SWFTags"; 66 | 67 | export { 68 | ErrorTypes, 69 | Telemetry, 70 | } from "./lib/factories/base/utilities/Telemetry"; 71 | 72 | export { StringUtilities } from "./lib/factories/base/utilities/StringUtilities"; 73 | export { 74 | clamp, 75 | roundHalfEven, 76 | } from "./lib/factories/base/utilities/NumberUtilities"; 77 | export { 78 | defineNonEnumerableProperty, 79 | getPropertyDescriptor, 80 | hasOwnProperty, 81 | copyOwnPropertyDescriptors, 82 | copyPropertiesByList, 83 | hasOwnGetter, 84 | defineReadOnlyProperty, 85 | toKeyValueArray, 86 | } from "./lib/factories/base/utilities/ObjectUtilities"; 87 | export { 88 | popManyInto, 89 | pushMany, 90 | } from "./lib/factories/base/utilities/ArrayUtilities"; 91 | 92 | export { ClipboardService } from "./lib/factories/base/utilities/ClipboardService"; 93 | 94 | export { 95 | clampS8U8, 96 | toS16, 97 | } from "./lib/factories/base/utilities/IntegerUtilities"; 98 | 99 | export { flashlog } from "./lib/factories/base/flashlog"; 100 | 101 | export { HashUtilities } from "./lib/factories/base/utilities/HashUtilities"; 102 | 103 | export { ExternalInterfaceService } from "./lib/factories/base/utilities/ExternalInterfaceService"; 104 | 105 | export { FileLoadingService } from "./lib/factories/base/utilities/FileLoadingService"; 106 | 107 | export { Stat, Record } from "./lib/stat/Stat"; 108 | -------------------------------------------------------------------------------- /lib/AVMEvent.ts: -------------------------------------------------------------------------------- 1 | import { EventBase } from '@awayjs/core'; 2 | import { AVMVERSION } from './factories/base/AVMVersion'; 3 | 4 | export class AVMEvent extends EventBase { 5 | private _avmVersion: AVMVERSION; 6 | 7 | /** 8 | * Dispatched when a AVM has been init (should only happen once for each AVM) 9 | */ 10 | public static AVM_COMPLETE: string = 'avmComplete'; 11 | 12 | constructor(type: string, avmVersion: AVMVERSION) { 13 | super(type); 14 | 15 | this._avmVersion = avmVersion; 16 | } 17 | 18 | /** 19 | * Additional human-readable message. Usually supplied for ParserEvent.PARSE_ERROR events. 20 | */ 21 | public get avmVersion(): AVMVERSION { 22 | return this._avmVersion; 23 | } 24 | 25 | public clone(): AVMEvent { 26 | return new AVMEvent(this.type, this._avmVersion); 27 | } 28 | } -------------------------------------------------------------------------------- /lib/AVMTestHandler.ts: -------------------------------------------------------------------------------- 1 | import { IInputRecorder, MouseManager } from '@awayjs/scene'; 2 | import { SWFFile } from './parsers/SWFFile'; 3 | let hasshownServerError: boolean = false; 4 | function serverError(error: any) { 5 | if (hasshownServerError) 6 | return; 7 | hasshownServerError = true; 8 | alert('Can not connect to Server. Is Server.js running ?'); 9 | 10 | } 11 | export enum TEST_MODE { 12 | TESTING = 'TESTING', 13 | RECORDING = 'RECORDING' 14 | } 15 | export class AVMTestHandler implements IInputRecorder { 16 | public frames: IAVMTestFrame[]; 17 | public events: StringMap; 18 | public frameIdx: number = 0; 19 | public swfInfo: ISWFTestInfo; 20 | public config: IAVMTestConfig; 21 | public reportedSWFInfos: boolean; 22 | public snapshotCnt: number = 0; 23 | public snapShotsUploaded: number = 0; 24 | private _finished: boolean; 25 | private _avmStage: any; 26 | 27 | constructor(config: IAVMTestConfig, avmStage: any) { 28 | this.config = config; 29 | this._avmStage = avmStage; 30 | this.frameIdx = 0; 31 | this.events = {}; 32 | this.reportedSWFInfos = false; 33 | this.frames = [{ 34 | messages: [], 35 | frameIdx: this.frameIdx 36 | }]; 37 | this._finished = false; 38 | this.snapshotCnt = 0; 39 | 40 | if (this.config.recordtest) 41 | MouseManager.inputRecorder = this; 42 | //if(this.config.mode==TEST_MODE.RECORDING){ 43 | // if recording, we must have a way to stop the recording, 44 | // so we listen for a keyboard sortcut here on window 45 | window.addEventListener('keydown', (event) => { 46 | if (event.ctrlKey && event.keyCode == 69) { 47 | this.finishTest(); 48 | } 49 | }); 50 | //} 51 | } 52 | 53 | /** 54 | * called from trace-function, to collect all traces for a frame 55 | * @param message 56 | */ 57 | public addMessage(message: string) { 58 | if (this._finished) 59 | return; 60 | this.frames[this.frames.length - 1].messages.push(message); 61 | this.checkIfFinished(); 62 | } 63 | 64 | public setSWF(swfFile: SWFFile) { 65 | this.swfInfo = { 66 | frameRate: swfFile.frameRate, 67 | swfVersion: swfFile.swfVersion, 68 | asVersion: swfFile.useAVM1 ? 2 : 3, 69 | width: swfFile.bounds.width / 20, 70 | height: swfFile.bounds.height / 20, 71 | }; 72 | } 73 | 74 | public recordEvent(event: any) { 75 | 76 | if (this.config.settings.snapShotOnMouseDown && event.type == 'mousedown') 77 | this.takeSnapshot(); 78 | 79 | if (this.config.settings.snapShotOnMouseUP && event.type == 'mouseup') 80 | this.takeSnapshot(); 81 | 82 | if (!this.config.recordtest) 83 | return; 84 | // todo allow to filter out all mousemove commands 85 | 86 | if (!this.config.settings.recordInput) 87 | return; 88 | 89 | //if(event.type==) 90 | 91 | if (!this.events[this.frameIdx]) 92 | this.events[this.frameIdx] = []; 93 | 94 | const jsonEvent = { 95 | type: event.type, 96 | screenX: event.screenX, 97 | screenY: event.screenY, 98 | clientX: event.clientX, 99 | clientY: event.clientY, 100 | ctrlKey: event.ctrlKey, 101 | altKey: event.altKey, 102 | shiftKey: event.shiftKey 103 | }; 104 | 105 | this.events[this.frameIdx].push(jsonEvent); 106 | } 107 | 108 | public dispatchEvents() { 109 | if (this.config.recordtest) 110 | return; 111 | if (this.config.events && this.config.events[this.frameIdx]) { 112 | for (let i = 0; i < this.config.events[this.frameIdx].length; i++) { 113 | const jsonEvent = this.config.events[this.frameIdx][i]; 114 | jsonEvent.preventDefault = () => { }; 115 | switch (jsonEvent.type) { 116 | case 'dblclick': 117 | MouseManager.getInstance(null).onDoubleClick(jsonEvent); 118 | break; 119 | case 'touchstart': 120 | MouseManager.getInstance(null).onMouseDown(jsonEvent); 121 | break; 122 | case 'mousedown': 123 | if (this.config.settings.snapShotOnMouseDown) 124 | this.takeSnapshot(); 125 | MouseManager.getInstance(null).onMouseDown(jsonEvent); 126 | break; 127 | case 'touchmove': 128 | MouseManager.getInstance(null).onMouseMove(jsonEvent); 129 | break; 130 | case 'mousemove': 131 | MouseManager.getInstance(null).onMouseMove(jsonEvent); 132 | break; 133 | case 'mouseup': 134 | if (this.config.settings.snapShotOnMouseUP) 135 | this.takeSnapshot(); 136 | MouseManager.getInstance(null).onMouseUp(jsonEvent); 137 | break; 138 | case 'touchend': 139 | MouseManager.getInstance(null).onMouseUp(jsonEvent); 140 | break; 141 | case 'mousewheel': 142 | MouseManager.getInstance(null).onMouseWheel(jsonEvent); 143 | break; 144 | case 'mouseover': 145 | MouseManager.getInstance(null).onMouseOver(jsonEvent); 146 | break; 147 | case 'mouseout': 148 | MouseManager.getInstance(null).onMouseOut(jsonEvent); 149 | break; 150 | case 'keydown': 151 | MouseManager.getInstance(null).onKeyDown(jsonEvent); 152 | break; 153 | case 'keyup': 154 | MouseManager.getInstance(null).onKeyUp(jsonEvent); 155 | break; 156 | 157 | } 158 | } 159 | } 160 | } 161 | 162 | public closeBrowserTab() { 163 | if (!this._finished) 164 | return; 165 | if ((this.snapShotsUploaded == 0 && this.snapshotCnt == 0) || this.snapShotsUploaded == this.snapshotCnt) { 166 | this.finishAndUploadTest(); 167 | } 168 | } 169 | 170 | public takeSnapshot() { 171 | if (this._finished) 172 | return; 173 | const myThis = this; 174 | const snapShotFrame = this.snapshotCnt++; 175 | this.addMessage('AWAYFLTEST SNAPSHOT ' + snapShotFrame); 176 | this._avmStage.snapshot(htmlCanvas => { 177 | 178 | htmlCanvas.toBlob((blob) => onBlob(blob)); 179 | function onBlob(blob) { 180 | console.log('snapshot done'); 181 | 182 | const formData = new FormData(); 183 | const fileName = 'snapshot_' + snapShotFrame + '.png'; 184 | formData.append('file', blob, fileName); 185 | 186 | const request = new XMLHttpRequest(); 187 | request.onreadystatechange = function (oEvent) { 188 | if (request.readyState == 4) { 189 | if (request.status == 200 || request.status == 0) { 190 | //console.log("AWAYFLTEST END"); 191 | myThis.snapShotsUploaded++; 192 | myThis.closeBrowserTab(); 193 | //console.log(request.responseText) 194 | } else { 195 | serverError(request.statusText + '-' + request.readyState + '-' + request.responseText + '-' + request.status); 196 | } 197 | } 198 | }; 199 | request.onerror = function (e) { 200 | serverError(request.statusText + '-' + request.readyState + '-' + request.responseText + '-' + request.status); 201 | }; 202 | try { 203 | request.open('POST', 'http://localhost:' + myThis.config.port + '/uploadImage', true); 204 | request.send(formData); 205 | } catch (e) { 206 | serverError('Could not save json on server. The Server.js is probably not running. Error: ' + e); 207 | } 208 | } 209 | }); 210 | } 211 | 212 | /** 213 | * called from onEnter on stage 214 | */ 215 | public nextFrame() { 216 | this.frameIdx++; 217 | if (this.config.settings.stopRecAfterFrame > 0 && this.frameIdx > this.config.settings.stopRecAfterFrame) { 218 | this.finishTest(); 219 | } 220 | if (this._finished) 221 | return; 222 | if (this.config.settings.snapShotFrames != 0 && this.frameIdx % this.config.settings.snapShotFrames == 0) 223 | this.takeSnapshot(); 224 | if (!this.config.settings.onlyTraces) { 225 | if (this.frames[this.frames.length - 1].messages.length > 0) { 226 | this.frames.push({ 227 | messages: [], 228 | frameIdx: this.frameIdx, 229 | }); 230 | } else { 231 | this.frames[this.frames.length - 1].frameIdx = this.frameIdx; 232 | } 233 | } 234 | 235 | } 236 | 237 | public checkIfFinished() { 238 | if (this.config.frames) { 239 | if (this.config.frames.length < this.frames.length) { 240 | // more test frames than recordet frames. test is complete (and propably failed) 241 | this.finishTest(); 242 | return; 243 | } 244 | let currentRecordetFrame: number = -1; 245 | for (let i = 0; i < this.config.frames.length; i++) { 246 | if (this.config.frames[i].frameIdx == this.frames[this.frames.length - 1].frameIdx) { 247 | currentRecordetFrame = i; 248 | } 249 | } 250 | if (currentRecordetFrame == -1) { 251 | // no recordet frame found for this frameIdx. test is complete (and propably failed) 252 | this.finishTest(); 253 | return; 254 | } 255 | if (currentRecordetFrame == this.config.frames.length - 1) { 256 | // last of the recordet frames, if test frame has same number of messages, test is complete 257 | const len1 = this.config.frames[currentRecordetFrame].messages.length; 258 | const len2 = this.frames[this.frames.length - 1].messages.length; 259 | if (len2 >= len1) { 260 | this.finishTest(); 261 | } 262 | } 263 | } 264 | } 265 | 266 | public finishTest() { 267 | if (this._finished) 268 | return; 269 | this._finished = true; 270 | this.closeBrowserTab(); 271 | } 272 | 273 | public finishAndUploadTest() { 274 | if (this.frames[this.frames.length - 1].messages.length == 0) { 275 | this.frames.pop(); 276 | } 277 | const myThis = this; 278 | const path = window.location.pathname; 279 | const page = path.split('/').pop().replace('.html', ''); 280 | const data = { 281 | player: 'awayflplayer', 282 | duration: Date.now() - this.config.startRecTime, 283 | date: new Date().toLocaleString(), 284 | url: page, 285 | swf: this.config.swfPath, 286 | swfInfos: this.swfInfo, 287 | settings: this.config.settings, 288 | frames: this.frames, 289 | events: this.events, 290 | seed: this.config.seed, 291 | }; 292 | const json = JSON.stringify(data); 293 | const formData = new FormData(); 294 | const blob = new Blob([json], { type: 'text/xml' }); 295 | if (this.config.recordtest) { 296 | var fileName = this.config.swfPath.replace(/\\/g, '/'); 297 | formData.append('file', blob, fileName); 298 | formData.append('record', 'true'); 299 | } else { 300 | var fileName = this.config.swfPath.replace(/\\/g, '/') + '/' + this.config.testPath; 301 | formData.append('file', blob, fileName); 302 | } 303 | const request = new XMLHttpRequest(); 304 | request.onreadystatechange = function (oEvent) { 305 | if (request.readyState == 4) { 306 | if (request.status == 200 || request.status == 0) { 307 | console.log('AWAYFLTEST END'); 308 | //window.close(); 309 | //console.log(request.responseText) 310 | } else { 311 | serverError(request.statusText + '-' + request.readyState + '-' + request.responseText + '-' + request.status); 312 | } 313 | } 314 | }; 315 | request.onerror = function (e) { 316 | serverError(request.statusText + '-' + request.readyState + '-' + request.responseText + '-' + request.status); 317 | }; 318 | try { 319 | request.open('POST', 'http://localhost:' + this.config.port + '/upload', true); 320 | request.send(formData); 321 | } catch (e) { 322 | serverError('Could not save json on server. The Server.js is probably not running. Error: ' + e); 323 | } 324 | } 325 | 326 | } 327 | export interface IAVMTestConfig { 328 | loadedFrames: IAVMTestFrame[]; 329 | seed: string; 330 | port: string; 331 | throwOnFail: boolean; 332 | mode: TEST_MODE; 333 | swf: string; 334 | swfPath: string; 335 | [key: string]: any; 336 | } 337 | export interface ISWFTestInfo { 338 | frameRate: number; 339 | width: number; 340 | height: number; 341 | asVersion: number; 342 | swfVersion: number; 343 | } 344 | export interface IAVMTestFrame { 345 | messages: string[]; 346 | frameIdx: number; 347 | } -------------------------------------------------------------------------------- /lib/IAVMHandler.ts: -------------------------------------------------------------------------------- 1 | import { SWFFile } from './parsers/SWFFile'; 2 | import { IAVMStage } from './IAVMStage'; 3 | import { ISceneGraphFactory } from '@awayjs/scene'; 4 | import { IAsset } from '@awayjs/core'; 5 | 6 | export interface IAVMHandler{ 7 | avmVersion: string; 8 | init(avmStage: IAVMStage, swfFile: SWFFile, callback: (hasInit: boolean) => void): void; 9 | factory: ISceneGraphFactory; 10 | addAsset(asset: IAsset, addScene: boolean); 11 | dispose(); 12 | resizeStage(); 13 | enterFrame(dt: number); 14 | } -------------------------------------------------------------------------------- /lib/IAVMStage.ts: -------------------------------------------------------------------------------- 1 | export interface IAVMStage{ 2 | // todo 3 | // right now it only exists to prevent circular dependency in IAVMHandler<->AVMStage 4 | } -------------------------------------------------------------------------------- /lib/factories/as3webFlash/display/StageAlign.ts: -------------------------------------------------------------------------------- 1 | export class StageAlign { 2 | public static BOTTOM: string = 'B'; 3 | public static BOTTOM_LEFT: string = 'BL'; 4 | public static BOTTOM_RIGHT: string = 'BR'; 5 | public static LEFT: string = 'L'; 6 | public static RIGHT: string = 'R'; 7 | public static TOP: string = 'T'; 8 | public static TOP_LEFT: string = 'TL'; 9 | public static TOP_RIGHT: string = 'TR'; 10 | 11 | } -------------------------------------------------------------------------------- /lib/factories/as3webFlash/display/StageScaleMode.ts: -------------------------------------------------------------------------------- 1 | export class StageScaleMode { 2 | public static EXACT_FIT: string = 'exactFit'; 3 | public static NO_BORDER: string = 'noBorder'; 4 | public static NO_SCALE: string = 'noScale'; 5 | public static SHOW_ALL: string = 'showAll'; 6 | 7 | } -------------------------------------------------------------------------------- /lib/factories/base/AVMVersion.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE! Not use a const literals of exported from index enums, 3 | * const enums is constantly replaced in compile time and really not exist in lib 4 | */ 5 | export enum AVMVERSION { 6 | AVM1 = 'AVM1', 7 | AVM2 = 'AVM2' 8 | } 9 | -------------------------------------------------------------------------------- /lib/factories/base/SWFTags.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"), 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { SwfTag, DefinitionTag, Bbox, ShapeRecord, FillStyle } from '@awayjs/graphics'; 17 | 18 | import { release } from './utilities/Debug'; 19 | import { IFilter } from '@awayjs/scene'; 20 | 21 | export enum FilterType { 22 | DROPSHADOW = 0, 23 | BLUR = 1, 24 | GLOW = 2, 25 | BEVEL = 3, 26 | GRADIENTGLOW = 4, 27 | CONVOLUTION = 5, 28 | COLORMATRIX = 6, 29 | GRADIENTBEVEL = 7, 30 | } 31 | 32 | export const enum SwfTagCode { 33 | CODE_END = 0, 34 | CODE_SHOW_FRAME = 1, 35 | CODE_DEFINE_SHAPE = 2, 36 | CODE_FREE_CHARACTER = 3, 37 | CODE_PLACE_OBJECT = 4, 38 | CODE_REMOVE_OBJECT = 5, 39 | CODE_DEFINE_BITS = 6, 40 | CODE_DEFINE_BUTTON = 7, 41 | CODE_JPEG_TABLES = 8, 42 | CODE_SET_BACKGROUND_COLOR = 9, 43 | CODE_DEFINE_FONT = 10, 44 | CODE_DEFINE_TEXT = 11, 45 | CODE_DO_ACTION = 12, 46 | CODE_DEFINE_FONT_INFO = 13, 47 | CODE_DEFINE_SOUND = 14, 48 | CODE_START_SOUND = 15, 49 | CODE_STOP_SOUND = 16, 50 | CODE_DEFINE_BUTTON_SOUND = 17, 51 | CODE_SOUND_STREAM_HEAD = 18, 52 | CODE_SOUND_STREAM_BLOCK = 19, 53 | CODE_DEFINE_BITS_LOSSLESS = 20, 54 | CODE_DEFINE_BITS_JPEG2 = 21, 55 | CODE_DEFINE_SHAPE2 = 22, 56 | CODE_DEFINE_BUTTON_CXFORM = 23, 57 | CODE_PROTECT = 24, 58 | CODE_PATHS_ARE_POSTSCRIPT = 25, 59 | CODE_PLACE_OBJECT2 = 26, 60 | // INVALID = 27, 61 | CODE_REMOVE_OBJECT2 = 28, 62 | CODE_SYNC_FRAME = 29, 63 | // INVALID = 30, 64 | CODE_FREE_ALL = 31, 65 | CODE_DEFINE_SHAPE3 = 32, 66 | CODE_DEFINE_TEXT2 = 33, 67 | CODE_DEFINE_BUTTON2 = 34, 68 | CODE_DEFINE_BITS_JPEG3 = 35, 69 | CODE_DEFINE_BITS_LOSSLESS2 = 36, 70 | CODE_DEFINE_EDIT_TEXT = 37, 71 | CODE_DEFINE_VIDEO = 38, 72 | CODE_DEFINE_SPRITE = 39, 73 | CODE_NAME_CHARACTER = 40, 74 | CODE_PRODUCT_INFO = 41, 75 | CODE_DEFINE_TEXT_FORMAT = 42, 76 | CODE_FRAME_LABEL = 43, 77 | CODE_DEFINE_BEHAVIOUR = 44, 78 | CODE_SOUND_STREAM_HEAD2 = 45, 79 | CODE_DEFINE_MORPH_SHAPE = 46, 80 | CODE_GENERATE_FRAME = 47, 81 | CODE_DEFINE_FONT2 = 48, 82 | CODE_GEN_COMMAND = 49, 83 | CODE_DEFINE_COMMAND_OBJECT = 50, 84 | CODE_CHARACTER_SET = 51, 85 | CODE_EXTERNAL_FONT = 52, 86 | CODE_DEFINE_FUNCTION = 53, 87 | CODE_PLACE_FUNCTION = 54, 88 | CODE_GEN_TAG_OBJECTS = 55, 89 | CODE_EXPORT_ASSETS = 56, 90 | CODE_IMPORT_ASSETS = 57, 91 | CODE_ENABLE_DEBUGGER = 58, 92 | CODE_DO_INIT_ACTION = 59, 93 | CODE_DEFINE_VIDEO_STREAM = 60, 94 | CODE_VIDEO_FRAME = 61, 95 | CODE_DEFINE_FONT_INFO2 = 62, 96 | CODE_DEBUG_ID = 63, 97 | CODE_ENABLE_DEBUGGER2 = 64, 98 | CODE_SCRIPT_LIMITS = 65, 99 | CODE_SET_TAB_INDEX = 66, 100 | // CODE_DEFINE_SHAPE4 = 67, 101 | // INVALID = 68, 102 | CODE_FILE_ATTRIBUTES = 69, 103 | CODE_PLACE_OBJECT3 = 70, 104 | CODE_IMPORT_ASSETS2 = 71, 105 | CODE_DO_ABC_DEFINE = 72, 106 | CODE_DEFINE_FONT_ALIGN_ZONES = 73, 107 | CODE_CSM_TEXT_SETTINGS = 74, 108 | CODE_DEFINE_FONT3 = 75, 109 | CODE_SYMBOL_CLASS = 76, 110 | CODE_METADATA = 77, 111 | CODE_DEFINE_SCALING_GRID = 78, 112 | // INVALID = 79, 113 | // INVALID = 80, 114 | // INVALID = 81, 115 | CODE_DO_ABC = 82, 116 | CODE_DEFINE_SHAPE4 = 83, 117 | CODE_DEFINE_MORPH_SHAPE2 = 84, 118 | // INVALID = 85, 119 | CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA = 86, 120 | CODE_DEFINE_BINARY_DATA = 87, 121 | CODE_DEFINE_FONT_NAME = 88, 122 | CODE_START_SOUND2 = 89, 123 | CODE_DEFINE_BITS_JPEG4 = 90, 124 | CODE_DEFINE_FONT4 = 91, 125 | CODE_TELEMETRY = 93 126 | } 127 | 128 | const SwfTagCodeNames = ['CODE_END','CODE_SHOW_FRAME','CODE_DEFINE_SHAPE','CODE_FREE_CHARACTER','CODE_PLACE_OBJECT','CODE_REMOVE_OBJECT','CODE_DEFINE_BITS','CODE_DEFINE_BUTTON','CODE_JPEG_TABLES','CODE_SET_BACKGROUND_COLOR','CODE_DEFINE_FONT','CODE_DEFINE_TEXT','CODE_DO_ACTION','CODE_DEFINE_FONT_INFO','CODE_DEFINE_SOUND','CODE_START_SOUND','CODE_STOP_SOUND','CODE_DEFINE_BUTTON_SOUND','CODE_SOUND_STREAM_HEAD','CODE_SOUND_STREAM_BLOCK','CODE_DEFINE_BITS_LOSSLESS','CODE_DEFINE_BITS_JPEG2','CODE_DEFINE_SHAPE2','CODE_DEFINE_BUTTON_CXFORM','CODE_PROTECT','CODE_PATHS_ARE_POSTSCRIPT','CODE_PLACE_OBJECT2','INVALID','CODE_REMOVE_OBJECT2','CODE_SYNC_FRAME','INVALID','CODE_FREE_ALL','CODE_DEFINE_SHAPE3','CODE_DEFINE_TEXT2','CODE_DEFINE_BUTTON2','CODE_DEFINE_BITS_JPEG3','CODE_DEFINE_BITS_LOSSLESS2','CODE_DEFINE_EDIT_TEXT','CODE_DEFINE_VIDEO','CODE_DEFINE_SPRITE','CODE_NAME_CHARACTER','CODE_PRODUCT_INFO','CODE_DEFINE_TEXT_FORMAT','CODE_FRAME_LABEL','CODE_DEFINE_BEHAVIOUR','CODE_SOUND_STREAM_HEAD2','CODE_DEFINE_MORPH_SHAPE','CODE_GENERATE_FRAME','CODE_DEFINE_FONT2','CODE_GEN_COMMAND','CODE_DEFINE_COMMAND_OBJECT','CODE_CHARACTER_SET','CODE_EXTERNAL_FONT','CODE_DEFINE_FUNCTION','CODE_PLACE_FUNCTION','CODE_GEN_TAG_OBJECTS','CODE_EXPORT_ASSETS','CODE_IMPORT_ASSETS','CODE_ENABLE_DEBUGGER','CODE_DO_INIT_ACTION','CODE_DEFINE_VIDEO_STREAM','CODE_VIDEO_FRAME','CODE_DEFINE_FONT_INFO2','CODE_DEBUG_ID','CODE_ENABLE_DEBUGGER2','CODE_SCRIPT_LIMITS','CODE_SET_TAB_INDEX','CODE_DEFINE_SHAPE4','INVALID','CODE_FILE_ATTRIBUTES','CODE_PLACE_OBJECT3','CODE_IMPORT_ASSETS2','CODE_DO_ABC_DEFINE','CODE_DEFINE_FONT_ALIGN_ZONES','CODE_CSM_TEXT_SETTINGS','CODE_DEFINE_FONT3','CODE_SYMBOL_CLASS','CODE_METADATA','CODE_DEFINE_SCALING_GRID','INVALID','INVALID','INVALID','CODE_DO_ABC','CODE_DEFINE_SHAPE4','CODE_DEFINE_MORPH_SHAPE2','INVALID','CODE_DEFINE_SCENE_AND_FRAME_LABEL_DATA','CODE_DEFINE_BINARY_DATA','CODE_DEFINE_FONT_NAME','CODE_START_SOUND2','CODE_DEFINE_BITS_JPEG4','CODE_DEFINE_FONT4','CODE_TELEMETRY']; 129 | 130 | export function getSwfTagCodeName(tagCode: SwfTagCode) { 131 | return release ? 'SwfTagCode: ' + tagCode : SwfTagCodeNames[tagCode]; 132 | } 133 | 134 | export enum DefinitionTags { 135 | CODE_DEFINE_SHAPE = 2, 136 | CODE_DEFINE_BITS = 6, 137 | CODE_DEFINE_BUTTON = 7, 138 | CODE_DEFINE_FONT = 10, 139 | CODE_DEFINE_TEXT = 11, 140 | CODE_DEFINE_SOUND = 14, 141 | CODE_DEFINE_BITS_LOSSLESS = 20, 142 | CODE_DEFINE_BITS_JPEG2 = 21, 143 | CODE_DEFINE_SHAPE2 = 22, 144 | CODE_DEFINE_SHAPE3 = 32, 145 | CODE_DEFINE_TEXT2 = 33, 146 | CODE_DEFINE_BUTTON2 = 34, 147 | CODE_DEFINE_BITS_JPEG3 = 35, 148 | CODE_DEFINE_BITS_LOSSLESS2 = 36, 149 | CODE_DEFINE_EDIT_TEXT = 37, 150 | CODE_DEFINE_SPRITE = 39, 151 | CODE_DEFINE_MORPH_SHAPE = 46, 152 | CODE_DEFINE_FONT2 = 48, 153 | CODE_DEFINE_VIDEO_STREAM = 60, 154 | CODE_DEFINE_FONT3 = 75, 155 | CODE_DEFINE_SHAPE4 = 83, 156 | CODE_DEFINE_MORPH_SHAPE2 = 84, 157 | CODE_DEFINE_BINARY_DATA = 87, 158 | CODE_DEFINE_BITS_JPEG4 = 90, 159 | CODE_DEFINE_FONT4 = 91 160 | } 161 | 162 | export enum ImageDefinitionTags { 163 | CODE_DEFINE_BITS = 6, 164 | CODE_DEFINE_BITS_JPEG2 = 21, 165 | CODE_DEFINE_BITS_JPEG3 = 35, 166 | CODE_DEFINE_BITS_JPEG4 = 90 167 | } 168 | 169 | export enum FontDefinitionTags { 170 | CODE_DEFINE_FONT = 10, 171 | CODE_DEFINE_FONT2 = 48, 172 | CODE_DEFINE_FONT3 = 75, 173 | CODE_DEFINE_FONT4 = 91 174 | } 175 | 176 | export enum ControlTags { 177 | CODE_PLACE_OBJECT = 4, 178 | CODE_PLACE_OBJECT2 = 26, 179 | CODE_PLACE_OBJECT3 = 70, 180 | CODE_REMOVE_OBJECT = 5, 181 | CODE_REMOVE_OBJECT2 = 28, 182 | CODE_START_SOUND = 15, 183 | CODE_START_SOUND2 = 89, 184 | CODE_VIDEO_FRAME = 61 185 | } 186 | 187 | export interface Matrix { 188 | a: number; 189 | b: number; 190 | c: number; 191 | d: number; 192 | tx: number; 193 | ty: number; 194 | } 195 | 196 | export interface ColorTransform { 197 | redMultiplier: number; 198 | greenMultiplier: number; 199 | blueMultiplier: number; 200 | alphaMultiplier: number; 201 | redOffset: number; 202 | greenOffset: number; 203 | blueOffset: number; 204 | alphaOffset: number; 205 | } 206 | 207 | export interface DisplayListTag extends SwfTag { 208 | depth: number; 209 | } 210 | 211 | export interface PlaceObjectTag extends DisplayListTag { 212 | actionBlocksPrecedence?: number; 213 | symbolId?: number; 214 | flags: number; 215 | matrix?: Matrix; 216 | cxform?: ColorTransform; 217 | className?: string; 218 | ratio?: number; 219 | name?: string; 220 | clipDepth?: number; 221 | filters?: any[]; 222 | blendMode?: number; 223 | bmpCache?: number; 224 | visibility?: boolean; 225 | backgroundColor?: number; 226 | events?: ClipEvents[]; 227 | } 228 | 229 | export const enum PlaceObjectFlags { 230 | Move = 0x0001, 231 | HasCharacter = 0x0002, 232 | HasMatrix = 0x0004, 233 | HasColorTransform = 0x0008, 234 | HasRatio = 0x0010, 235 | HasName = 0x0020, 236 | HasClipDepth = 0x0040, 237 | HasClipActions = 0x0080, 238 | HasFilterList = 0x0100, 239 | HasBlendMode = 0x0200, 240 | HasCacheAsBitmap = 0x0400, 241 | HasClassName = 0x0800, 242 | HasImage = 0x1000, 243 | HasVisible = 0x2000, 244 | OpaqueBackground = 0x4000, 245 | Reserved = 0x8000 246 | } 247 | 248 | export const enum AVM1ClipEvents { 249 | Load = 0x00001, 250 | EnterFrame = 0x00002, 251 | Unload = 0x00004, 252 | MouseMove = 0x00008, 253 | MouseDown = 0x00010, 254 | MouseUp = 0x00020, 255 | KeyDown = 0x00040, 256 | KeyUp = 0x00080, 257 | Data = 0x00100, 258 | Initialize = 0x00200, 259 | Press = 0x00400, 260 | Release = 0x00800, 261 | ReleaseOutside = 0x01000, 262 | RollOver = 0x02000, 263 | RollOut = 0x04000, 264 | DragOver = 0x08000, 265 | DragOut = 0x10000, 266 | KeyPress = 0x20000, 267 | Construct = 0x40000 268 | } 269 | 270 | export interface ClipEvents { 271 | flags: number; 272 | keyCode?: number; 273 | actionsBlock: Uint8Array; 274 | } 275 | 276 | export interface Filter extends IFilter { 277 | 278 | } 279 | 280 | export interface GlowFilter extends Filter { 281 | colors: number[]; 282 | ratios?: number[]; 283 | blurX: number; 284 | blurY: number; 285 | angle?: number; 286 | distance?: number; 287 | strength: number; 288 | inner: boolean; 289 | knockout: boolean; 290 | compositeSource: boolean; 291 | onTop?: boolean; 292 | quality: number; 293 | } 294 | 295 | export interface BlurFilter extends Filter { 296 | blurX: number; 297 | blurY: number; 298 | quality: number; 299 | } 300 | 301 | export interface ConvolutionFilter extends Filter { 302 | matrixX: number; 303 | matrixY: number; 304 | divisor: number; 305 | bias: number; 306 | matrix: number[]; 307 | color: number; 308 | clamp: boolean; 309 | preserveAlpha: boolean; 310 | } 311 | 312 | export interface ColorMatrixFilter extends Filter { 313 | matrix: number[]; 314 | } 315 | 316 | export interface RemoveObjectTag extends DisplayListTag { 317 | depth: number; 318 | symbolId?: number; 319 | } 320 | 321 | export interface ImageTag extends DefinitionTag { 322 | deblock?: number; 323 | imgData: Uint8Array; 324 | alphaData?: Uint8Array; 325 | mimeType: string; 326 | jpegTables?: { data: Uint8Array }; 327 | } 328 | 329 | export interface ButtonTag extends DefinitionTag { 330 | characters?: ButtonCharacter[]; 331 | actionsData?: Uint8Array; 332 | trackAsMenu?: boolean; 333 | buttonActions?: ButtonCondAction[]; 334 | } 335 | 336 | export interface ButtonCharacter { 337 | flags: number; 338 | symbolId?: number; 339 | depth?: number; 340 | matrix?: Matrix; 341 | cxform?: ColorTransform; 342 | filters?: Filter[]; 343 | blendMode?: number; 344 | buttonActions?: ButtonCondAction[]; 345 | } 346 | 347 | export const enum ButtonCharacterFlags { 348 | StateUp = 0x01, 349 | StateOver = 0x02, 350 | StateDown = 0x04, 351 | StateHitTest = 0x08, 352 | HasFilterList = 0x10, 353 | HasBlendMode = 0x20 354 | } 355 | 356 | export interface ButtonCondAction { 357 | keyCode: number; 358 | stateTransitionFlags: number; 359 | actionsData: Uint8Array; 360 | } 361 | 362 | export interface BinaryDataTag extends DefinitionTag { 363 | data: Uint8Array; 364 | } 365 | 366 | export interface FontTag extends DefinitionTag { 367 | flags: number; 368 | language?: number; 369 | name: string; 370 | fontStyleName?: string; 371 | copyright?: string; 372 | resolution?: number; 373 | offsets?: number[]; 374 | mapOffset?: number; 375 | glyphs?: Glyph[]; 376 | codes?: number[]; 377 | ascent?: number; 378 | descent?: number; 379 | leading?: number; 380 | advance?: number[]; 381 | bbox?: Bbox[]; 382 | kerning?: Kerning[]; 383 | data?: Uint8Array; 384 | } 385 | 386 | export const enum FontFlags { 387 | Bold = 0x01, 388 | Italic = 0x02, 389 | WideOrHasFontData = 0x04, 390 | WideOffset = 0x08, 391 | Ansi = 0x10, 392 | SmallText = 0x20, 393 | ShiftJis = 0x40, 394 | HasLayout = 0x80 395 | } 396 | 397 | export type Glyph = ShapeRecord[]; 398 | 399 | export interface StaticTextTag extends DefinitionTag { 400 | bbox: Bbox; 401 | matrix: Matrix; 402 | records: TextRecord[]; 403 | } 404 | 405 | export interface TextRecord { 406 | flags: number; 407 | fontId?: number; 408 | color?: number; 409 | moveX?: number; 410 | moveY?: number; 411 | fontHeight?: number; 412 | glyphCount?: number; 413 | entries?: TextEntry[]; 414 | } 415 | 416 | export const enum TextRecordFlags { 417 | HasMoveX = 0x01, 418 | HasMoveY = 0x02, 419 | HasColor = 0x04, 420 | HasFont = 0x08 421 | } 422 | 423 | export interface TextEntry { 424 | glyphIndex: number; 425 | advance: number; 426 | } 427 | 428 | export interface SoundTag extends DefinitionTag { 429 | soundFormat: number; 430 | soundRate: number; 431 | soundSize: number; 432 | soundType: number; 433 | samplesCount: number; 434 | soundData: Uint8Array; 435 | } 436 | 437 | export interface StartSoundTag extends SwfTag { 438 | soundId?: number; 439 | soundClassName?: string; 440 | soundInfo: SoundInfo; 441 | } 442 | 443 | export interface SoundInfo { 444 | flags: number; 445 | inPoint?: number; 446 | outPoint?: number; 447 | loopCount?: number; 448 | envelopes?: SoundEnvelope[]; 449 | } 450 | 451 | export const enum SoundInfoFlags { 452 | HasInPoint = 0x01, 453 | HasOutPoint = 0x02, 454 | HasLoops = 0x04, 455 | HasEnvelope = 0x08, 456 | NoMultiple = 0x10, 457 | Stop = 0x20 458 | } 459 | 460 | export interface SoundEnvelope { 461 | pos44: number; 462 | volumeLeft: number; 463 | volumeRight: number; 464 | } 465 | 466 | export interface SoundStreamHeadTag { 467 | playbackRate: number; 468 | playbackSize: number; 469 | playbackType: number; 470 | streamCompression: number; 471 | streamRate: number; 472 | streamSize: number; 473 | streamType: number; 474 | samplesCount: number; 475 | latencySeek?: number; 476 | } 477 | 478 | export interface BitmapTag extends DefinitionTag { 479 | format: number; 480 | width: number; 481 | height: number; 482 | hasAlpha: boolean; 483 | // Number of color table entries - 1, not size in bytes. 484 | colorTableSize?: number; 485 | bmpData: Uint8Array; 486 | } 487 | 488 | export interface TextTag extends DefinitionTag { 489 | bbox: Bbox; 490 | flags: number; 491 | fontId?: number; 492 | fontClass?: string; 493 | fontHeight?: number; 494 | color?: number; 495 | maxLength?: number; 496 | align?: number; 497 | leftMargin?: number; 498 | rightMargin?: number; 499 | indent?: number; 500 | leading?: number; 501 | variableName: string; 502 | initialText?: string; 503 | } 504 | 505 | export const enum TextFlags { 506 | HasFont = 0x0001, 507 | HasMaxLength = 0x0002, 508 | HasColor = 0x0004, 509 | ReadOnly = 0x0008, 510 | Password = 0x0010, 511 | Multiline = 0x0020, 512 | WordWrap = 0x0040, 513 | HasText = 0x0080, 514 | UseOutlines = 0x0100, 515 | Html = 0x0200, 516 | WasStatic = 0x0400, 517 | Border = 0x0800, 518 | NoSelect = 0x1000, 519 | HasLayout = 0x2000, 520 | AutoSize = 0x4000, 521 | HasFontClass = 0x8000 522 | } 523 | 524 | export interface Kerning { 525 | code1: number; 526 | code2: number; 527 | adjustment: number; 528 | } 529 | 530 | export interface ScalingGridTag extends SwfTag { 531 | symbolId: number; 532 | splitter: Bbox; 533 | } 534 | 535 | export interface SceneTag extends SwfTag { 536 | scenes: Scene[]; 537 | labels: Label[]; 538 | } 539 | 540 | export interface Scene { 541 | offset: number; 542 | name: string; 543 | labels: Label[]; 544 | numFrames: number; 545 | } 546 | 547 | export interface Label { 548 | frame: number; 549 | name: string; 550 | } 551 | 552 | export interface ShapeTag extends DefinitionTag { 553 | lineBounds: Bbox; 554 | lineBoundsMorph?: Bbox; 555 | fillBounds?: Bbox; 556 | fillBoundsMorph?: Bbox; 557 | flags: number; 558 | fillStyles: FillStyle[]; 559 | lineStyles: LineStyle[]; 560 | records: ShapeRecord[]; 561 | recordsMorph?: ShapeRecord[]; 562 | } 563 | 564 | export const enum ShapeFlags { 565 | UsesScalingStrokes = 0x01, 566 | UsesNonScalingStrokes = 0x02, 567 | UsesFillWindingRule = 0x04, 568 | IsMorph = 0x08 569 | } 570 | 571 | export interface SolidFill extends FillStyle { 572 | color: number; 573 | colorMorph?: number; 574 | } 575 | 576 | export interface GradientFill extends FillStyle { 577 | matrix: Matrix; 578 | matrixMorph?: Matrix; 579 | spreadMode?: number; 580 | interpolationMode?: number; 581 | records: GradientRecord[]; 582 | focalPoint?: number; 583 | focalPointMorph?: number; 584 | } 585 | 586 | export interface GradientRecord { 587 | ratio: number; 588 | color: number; 589 | ratioMorph?: number; 590 | colorMorph?: number; 591 | } 592 | 593 | export interface BitmapFill extends FillStyle { 594 | bitmapId: number; 595 | condition: boolean; 596 | matrix: Matrix; 597 | matrixMorph?: Matrix; 598 | } 599 | 600 | export interface LineStyle { 601 | width: number; 602 | widthMorph?: number; 603 | startCapsStyle?: number; 604 | jointStyle?: number; 605 | hasFill?: number; 606 | noHscale?: boolean; 607 | noVscale?: boolean; 608 | pixelHinting?: boolean; 609 | noClose?: boolean; 610 | endCapsStyle?: number; 611 | miterLimitFactor?: number; 612 | fillStyle?: FillStyle; 613 | color?: number; 614 | colorMorph?: number; 615 | } 616 | 617 | export const enum ShapeRecordFlags { 618 | Move = 0x01, 619 | HasFillStyle0 = 0x02, 620 | HasFillStyle1 = 0x04, 621 | HasLineStyle = 0x08, 622 | HasNewStyles = 0x10, 623 | IsStraight = 0x20, 624 | IsGeneral = 0x40, 625 | IsVertical = 0x80 626 | } 627 | 628 | export interface VideoStreamTag extends DefinitionTag { 629 | numFrames: number; 630 | width: number; 631 | height: number; 632 | deblocking: number; 633 | smoothing: boolean; 634 | codecId: number; 635 | } 636 | 637 | export interface VideoFrameTag extends SwfTag { 638 | streamId: number; 639 | frameNum: number; 640 | videoData: Uint8Array; 641 | } 642 | -------------------------------------------------------------------------------- /lib/factories/base/binaryFileReader.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | //module Shumway { 18 | declare let XMLHttpRequest; 19 | 20 | export interface BinaryFileReaderProgressInfo { 21 | loaded: number; 22 | total: number; 23 | } 24 | 25 | export class BinaryFileReader { 26 | url: string; 27 | method: string; 28 | mimeType: string; 29 | data: any; 30 | xhr: XMLHttpRequest; 31 | 32 | constructor(url: string, method?: string, mimeType?: string, data?) { 33 | this.url = url; 34 | this.method = method; 35 | this.mimeType = mimeType; 36 | this.data = data; 37 | } 38 | 39 | readAll(progress: (response: any, loaded: number, total: number) => void, 40 | complete: (response: any, error?: any) => void) { 41 | const url = this.url; 42 | const xhr = this.xhr = new XMLHttpRequest({ mozSystem: true }); 43 | const async = true; 44 | xhr.open(this.method || 'GET', this.url, async); 45 | xhr.responseType = 'arraybuffer'; 46 | if (progress) { 47 | xhr.onprogress = function (event) { 48 | progress(xhr.response, event.loaded, event.total); 49 | }; 50 | } 51 | xhr.onreadystatechange = function (event) { 52 | if (xhr.readyState === 4) { 53 | if (xhr.status !== 200 && xhr.status !== 0 || xhr.response === null) { 54 | unexpected('Path: ' + url + ' not found.'); 55 | complete(null, xhr.statusText); 56 | return; 57 | } 58 | complete(xhr.response); 59 | } 60 | }; 61 | if (this.mimeType) { 62 | xhr.setRequestHeader('Content-Type', this.mimeType); 63 | } 64 | xhr.send(this.data || null); 65 | } 66 | 67 | readChunked(chunkSize: number /* int */, 68 | ondata: (data: Uint8Array, progress: BinaryFileReaderProgressInfo) => void, 69 | onerror: (err: any) => void, 70 | onopen?: () => void, 71 | oncomplete?: () => void, 72 | onhttpstatus?: (location: string, status: string, responseHeaders: any) => void) { 73 | if (chunkSize <= 0) { 74 | this.readAsync(ondata, onerror, onopen, oncomplete, onhttpstatus); 75 | return; 76 | } 77 | 78 | let position = 0; 79 | const buffer = new Uint8Array(chunkSize); 80 | let read = 0, total; 81 | this.readAsync( 82 | function (data: Uint8Array, progress: BinaryFileReaderProgressInfo) { 83 | total = progress.total; 84 | let left = data.length, offset = 0; 85 | while (position + left >= chunkSize) { 86 | const tailSize = chunkSize - position; 87 | buffer.set(data.subarray(offset, offset + tailSize), position); 88 | offset += tailSize; 89 | left -= tailSize; 90 | read += chunkSize; 91 | ondata(buffer, { loaded: read, total: total }); 92 | position = 0; 93 | } 94 | buffer.set(data.subarray(offset), position); 95 | position += left; 96 | }, 97 | onerror, 98 | onopen, 99 | function () { 100 | if (position > 0) { 101 | read += position; 102 | ondata(buffer.subarray(0, position), { loaded: read, total: total }); 103 | position = 0; 104 | } 105 | oncomplete && oncomplete(); 106 | }, 107 | onhttpstatus); 108 | } 109 | 110 | readAsync(ondata: (data: Uint8Array, progress: BinaryFileReaderProgressInfo) => void, 111 | onerror: (err: any) => void, 112 | onopen?: () => void, 113 | oncomplete?: () => void, 114 | onhttpstatus?: (location: string, status: string, responseHeaders: any) => void) { 115 | const xhr = this.xhr = new XMLHttpRequest({ mozSystem: true }); 116 | const url = this.url; 117 | let loaded = 0; 118 | let total = 0; 119 | xhr.open(this.method || 'GET', url, true); 120 | xhr.responseType = 'moz-chunked-arraybuffer'; 121 | const isNotProgressive = xhr.responseType !== 'moz-chunked-arraybuffer'; 122 | if (isNotProgressive) { 123 | xhr.responseType = 'arraybuffer'; 124 | } 125 | xhr.onprogress = function (e) { 126 | if (isNotProgressive) { 127 | return; 128 | } 129 | loaded = e.loaded; 130 | total = e.total; 131 | const bytes = new Uint8Array(xhr.response); 132 | // The event's `loaded` and `total` properties are sometimes lower than the actual 133 | // number of loaded bytes. In that case, increase them to that value. 134 | loaded = Math.max(loaded, bytes.byteLength); 135 | total = Math.max(total, bytes.byteLength); 136 | ondata(bytes, { loaded: loaded, total: total }); 137 | }; 138 | xhr.onreadystatechange = function (event) { 139 | if (xhr.readyState === 2 && onhttpstatus) { 140 | onhttpstatus(url, xhr.status, xhr.getAllResponseHeaders()); 141 | } 142 | if (xhr.readyState === 4) { 143 | // Failed loads can be detected through either the status code or the fact that nothing 144 | // has been loaded. 145 | // Note: Just checking that `xhr.response` is set doesn't work, as Firefox enables 146 | // chunked loading, and in that mode `response` is only set in the `onprogress` handler. 147 | if (xhr.status !== 200 && xhr.status !== 0 || 148 | xhr.response === null && (total === 0 || loaded !== total)) { 149 | onerror(xhr.statusText); 150 | return; 151 | } 152 | if (isNotProgressive) { 153 | const buffer = xhr.response; 154 | ondata(new Uint8Array(buffer), { loaded: buffer.byteLength, total: buffer.byteLength }); 155 | } 156 | } 157 | }; 158 | xhr.onload = function () { 159 | if (oncomplete) { 160 | oncomplete(); 161 | } 162 | }; 163 | if (this.mimeType) { 164 | xhr.setRequestHeader('Content-Type', this.mimeType); 165 | } 166 | xhr.send(this.data || null); 167 | if (onopen) { 168 | onopen(); 169 | } 170 | } 171 | 172 | abort() { 173 | if (this.xhr) { 174 | this.xhr.abort(); 175 | this.xhr = null; 176 | } 177 | } 178 | } 179 | //} 180 | -------------------------------------------------------------------------------- /lib/factories/base/external.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | ILocalConnectionSender, LocalConnectionCloseResult, 19 | LocalConnectionConnectResult 20 | } from './utilities/LocalConnectionService'; 21 | 22 | const ShumwayEnvironment = { 23 | DEBUG: 'test', 24 | DEVELOPMENT: 'dev', 25 | RELEASE: 'release', 26 | TEST: 'test' 27 | }; 28 | 29 | export var ShumwayCom = { 30 | 'getWeakMapKeys':function (param: any) {return null;}, 31 | 32 | }; 33 | /* 34 | export declare var ShumwayCom { 35 | environment: string // ShumwayEnvironment; 36 | 37 | createSpecialInflate?: () => SpecialInflate; 38 | createRtmpSocket?: (options: {host: string; port: number; ssl: boolean}) => RtmpSocket; 39 | createRtmpXHR?: () => RtmpXHR; 40 | 41 | createSpecialStorage: () => SpecialStorage; 42 | getWeakMapKeys: (weakMap) => Array; 43 | fallback: () => void; 44 | reportIssue: (details?: string) => void; 45 | reportTelemetry: (data) => void; 46 | enableDebug: () => void; 47 | getPluginParams: () => any; 48 | getSettings: () => any; 49 | setClipboard: (data: string) => void; 50 | setFullscreen: (enabled: boolean) => void; 51 | externalCom: (args: any) => any; 52 | loadFile: (args: any) => void; 53 | abortLoad: (sessionId: number) => void; 54 | loadSystemResource: (id: number) => void; 55 | navigateTo: (args: any) => void; 56 | setupComBridge: (playerWindow: any) => void; 57 | sendSyncMessage: (data: any) => any; 58 | postAsyncMessage: (data: any) => void; 59 | 60 | setLoadFileCallback: (callback: (data) => void) => void; 61 | setExternalCallback: (callback: (call) => any) => void; 62 | setSystemResourceCallback: (callback: (id: number, data: any) => void) => void; 63 | setSyncMessageCallback: (callback: (data: any) => any) => void; 64 | setAsyncMessageCallback: (callback: (data: any) => void) => void; 65 | 66 | getLocalConnectionService: () => LocalConnectionService; 67 | 68 | processFrame?: () => void; 69 | processFSCommand?: (command: string, args: any) => void; 70 | print?: (msg: string) => void; 71 | }; 72 | */ 73 | 74 | interface SpecialStorage { 75 | getItem(key: string): string; 76 | setItem(key: string, value: string): void; 77 | removeItem(key: string): void; 78 | } 79 | 80 | interface SpecialInflate { 81 | setDataCallback(callback: (data: Uint8Array) => void): void; 82 | push(data: Uint8Array); 83 | close(); 84 | } 85 | 86 | interface LocalConnectionService { 87 | createLocalConnection: (connectionName: string, 88 | callback: (methodName: string, argsBuffer: ArrayBuffer) => any 89 | ) => LocalConnectionConnectResult; 90 | hasLocalConnection: (connectionName: string) => boolean; 91 | closeLocalConnection: (connectionName: string) => LocalConnectionCloseResult; 92 | sendLocalConnectionMessage: (connectionName: string, methodName: string, 93 | argsBuffer: ArrayBuffer, sender: ILocalConnectionSender, 94 | senderDomain: string, senderIsSecure: boolean) => void; 95 | allowDomainsForLocalConnection: (connectionName: string, domains: string[], 96 | secure: boolean) => void; 97 | } 98 | 99 | interface RtmpSocket { 100 | setOpenCallback(callback: () => void): void; 101 | setDataCallback(callback: (e: {data: ArrayBuffer}) => void): void; 102 | setDrainCallback(callback: () => void): void; 103 | setErrorCallback(callback: (e: any) => void): void; 104 | setCloseCallback(callback: () => void): void; 105 | 106 | send(buffer: ArrayBuffer, offset: number, count: number): boolean; 107 | close(): void; 108 | } 109 | 110 | interface RtmpXHR { 111 | status: number; 112 | response: any; 113 | responseType: string; 114 | 115 | setLoadCallback(callback: () => void): void; 116 | setErrorCallback(callback: () => void): void; 117 | 118 | open(method: string, path: string, async?: boolean): void; 119 | setRequestHeader(header: string, value: string): void; 120 | send(data?: any): void; 121 | } 122 | -------------------------------------------------------------------------------- /lib/factories/base/flashlog.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Produces similar output as flashlog.txt It can be produced by the 18 | // debug builds of Flash Player. 19 | // See https://github.com/mozilla/shumway/wiki/Trace-Output-with-Flash-Player-Debugger 20 | import { abstractMethod } from './utilities/Debug'; 21 | 22 | export class FlashLog { 23 | public isAS3TraceOn: boolean = true; 24 | 25 | private _startTime: number; 26 | 27 | public constructor() { 28 | this._startTime = Date.now(); 29 | } 30 | 31 | public get currentTimestamp(): number { 32 | return Date.now() - this._startTime; 33 | } 34 | 35 | _writeLine(line: string): void { 36 | abstractMethod('FlashLog._writeLine'); 37 | } 38 | 39 | public writeAS3Trace(msg: string): void { 40 | if (this.isAS3TraceOn) { 41 | this._writeLine(this.currentTimestamp + ' AVMINF: ' + msg); 42 | } 43 | } 44 | } 45 | 46 | export var flashlog: FlashLog = null; 47 | -------------------------------------------------------------------------------- /lib/factories/base/metrics.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { getTicks, IndentingWriter } from './utilities'; 18 | 19 | export class Timer { 20 | private static _base: Timer = new Timer(null, 'Total'); 21 | private static _top: Timer = Timer._base; 22 | private static _flat: Timer = new Timer(null, 'Flat'); 23 | private static _flatStack: Timer[] = []; 24 | 25 | private _begin: number = 0; 26 | private _last: number = 0; 27 | private _total: number = 0; 28 | private _count: number = 0; 29 | private _timers: Record = {}; 30 | 31 | constructor( 32 | private _parent: Timer | null, 33 | private _name: string 34 | ) {} 35 | 36 | public static time(name, fn: Function) { 37 | Timer.start(name); 38 | fn(); 39 | Timer.stop(); 40 | } 41 | 42 | public static start(name) { 43 | Timer._top = Timer._top._timers[name] || (Timer._top._timers[name] = new Timer(Timer._top, name)); 44 | Timer._top.start(); 45 | const tmp = Timer._flat._timers[name] || (Timer._flat._timers[name] = new Timer(Timer._flat, name)); 46 | tmp.start(); 47 | Timer._flatStack.push(tmp); 48 | } 49 | 50 | public static stop() { 51 | Timer._top.stop(); 52 | const parent = Timer._top._parent; 53 | 54 | if (parent) { 55 | Timer._top = parent; 56 | Timer._flatStack.pop()?.stop(); 57 | } 58 | } 59 | 60 | public static stopStart(name) { 61 | Timer.stop(); 62 | Timer.start(name); 63 | } 64 | 65 | public start() { 66 | this._begin = getTicks(); 67 | } 68 | 69 | public stop() { 70 | this._last = getTicks() - this._begin; 71 | this._total += this._last; 72 | this._count += 1; 73 | } 74 | 75 | public toJSON() { 76 | return { name: this._name, total: this._total, timers: this._timers }; 77 | } 78 | 79 | public trace(writer: IndentingWriter) { 80 | writer.enter ( 81 | this._name + ': ' + this._total.toFixed(2) + ' ms' + 82 | ', count: ' + this._count + 83 | ', average: ' + (this._total / this._count).toFixed(2) + ' ms' 84 | ); 85 | for (const name in this._timers) { 86 | this._timers[name].trace(writer); 87 | } 88 | writer.outdent(); 89 | } 90 | 91 | public static trace(writer: IndentingWriter) { 92 | Timer._base.trace(writer); 93 | Timer._flat.trace(writer); 94 | } 95 | } 96 | 97 | /** 98 | * Quick way to count named events. 99 | */ 100 | export class Counter { 101 | public static instance: Counter = new Counter(true); 102 | 103 | private _counts: Record = {}; 104 | private _times: Record = {}; 105 | 106 | public get counts(): Record { 107 | return this._counts; 108 | } 109 | 110 | constructor( 111 | private _enabled: boolean 112 | ) {} 113 | 114 | public setEnabled(enabled: boolean) { 115 | this._enabled = enabled; 116 | } 117 | 118 | public clear() { 119 | this._counts = {}; 120 | this._times = {}; 121 | } 122 | 123 | public toJSON() { 124 | return { 125 | counts: this._counts, 126 | times: this._times 127 | }; 128 | } 129 | 130 | public count(name: string, increment: number = 1, time: number = 0) { 131 | if (!this._enabled) { 132 | return; 133 | } 134 | if (this._counts[name] === undefined) { 135 | this._counts[name] = 0; 136 | this._times[name] = 0; 137 | } 138 | this._counts[name] += increment; 139 | this._times[name] += time; 140 | return this._counts[name]; 141 | } 142 | 143 | public trace(writer: IndentingWriter) { 144 | for (const name in this._counts) { 145 | writer.writeLn(name + ': ' + this._counts[name]); 146 | } 147 | } 148 | 149 | private _pairToString(times: Record, pair: [string, number]): string { 150 | const name = pair[0]; 151 | const count = pair[1]; 152 | const time = times[name]; 153 | let line = name + ': ' + count; 154 | if (time) { 155 | line += ', ' + time.toFixed(4); 156 | if (count > 1) { 157 | line += ' (' + (time / count).toFixed(4) + ')'; 158 | } 159 | } 160 | return line; 161 | } 162 | 163 | public toStringSorted(): string { 164 | const times = this._times; 165 | const pairs: [string, number][] = []; 166 | for (const name in this._counts) { 167 | pairs.push([name, this._counts[name]]); 168 | } 169 | pairs.sort(function (a, b) { 170 | return b[1] - a[1]; 171 | }); 172 | return (pairs.map(pair => this._pairToString(times, pair)).join(', ')); 173 | } 174 | 175 | public traceSorted(writer: IndentingWriter, inline = false) { 176 | const times = this._times; 177 | const pairs: [string, number][] = []; 178 | for (const name in this._counts) { 179 | pairs.push([name, this._counts[name]]); 180 | } 181 | pairs.sort((a, b) => b[1] - a[1]); 182 | if (inline) { 183 | writer.writeLn(pairs.map(pair => this._pairToString(times, pair)).join(', ')); 184 | } else { 185 | pairs.forEach(pair => writer.writeLn(this._pairToString(times, pair))); 186 | } 187 | } 188 | } 189 | 190 | export class Average { 191 | private _samples: Float64Array; 192 | private _count: number; 193 | private _index: number; 194 | 195 | constructor(max) { 196 | this._samples = new Float64Array(max); 197 | this._count = 0; 198 | this._index = 0; 199 | } 200 | 201 | public push(sample: number) { 202 | if (this._count < this._samples.length) { 203 | this._count++; 204 | } 205 | this._index++; 206 | this._samples[this._index % this._samples.length] = sample; 207 | } 208 | 209 | public average(): number { 210 | let sum = 0; 211 | for (let i = 0; i < this._count; i++) { 212 | sum += this._samples[i]; 213 | } 214 | return sum / this._count; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /lib/factories/base/options.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { assert, release } from './utilities/Debug'; 18 | import { isObject } from './utilities'; 19 | 20 | /** 21 | * Option and Argument Management 22 | * 23 | * Options are configuration settings sprinkled throughout the code. They can be grouped into sets of 24 | * options called |OptionSets| which can form a hierarchy of options. For instance: 25 | * 26 | * var set = new OptionSet(); 27 | * var opt = set.register(new Option("v", "verbose", "boolean", false, "Enables verbose logging.")); 28 | * 29 | * creates an option set with one option in it. The option can be changed directly using |opt.value = true| or 30 | * automatically using the |ArgumentParser|: 31 | * 32 | * var parser = new ArgumentParser(); 33 | * parser.addBoundOptionSet(set); 34 | * parser.parse(["-v"]); 35 | * 36 | * The |ArgumentParser| can also be used directly: 37 | * 38 | * var parser = new ArgumentParser(); 39 | * argumentParser.addArgument("h", "help", "boolean", {parse: function (x) { 40 | * printUsage(); 41 | * }}); 42 | */ 43 | 44 | export class Argument { 45 | shortName: string; 46 | longName: string; 47 | type: any; 48 | options: any; 49 | positional: boolean; 50 | parseFn: any; 51 | value: any; 52 | constructor(shortName, longName, type, options) { 53 | this.shortName = shortName; 54 | this.longName = longName; 55 | this.type = type; 56 | options = options || {}; 57 | this.positional = options.positional; 58 | this.parseFn = options.parse; 59 | this.value = options.defaultValue; 60 | } 61 | 62 | public parse(value) { 63 | if (this.type === 'boolean') { 64 | release || assert(typeof value === 'boolean'); 65 | this.value = value; 66 | } else if (this.type === 'number') { 67 | release || assert(!isNaN(value), value + ' is not a number'); 68 | this.value = parseInt(value, 10); 69 | } else { 70 | this.value = value; 71 | } 72 | if (this.parseFn) { 73 | this.parseFn(this.value); 74 | } 75 | } 76 | } 77 | 78 | export class ArgumentParser { 79 | args: any []; 80 | constructor() { 81 | this.args = []; 82 | } 83 | 84 | public addArgument(shortName, longName, type, options) { 85 | const argument = new Argument(shortName, longName, type, options); 86 | this.args.push(argument); 87 | return argument; 88 | } 89 | 90 | public addBoundOption(option) { 91 | const options = { parse: function (x) { 92 | option.value = x; 93 | } }; 94 | this.args.push(new Argument(option.shortName, option.longName, option.type, options)); 95 | } 96 | 97 | public addBoundOptionSet(optionSet) { 98 | const self = this; 99 | optionSet.options.forEach(function (x) { 100 | if (OptionSet.isOptionSet(x)) { 101 | self.addBoundOptionSet(x); 102 | } else { 103 | release || assert(x); 104 | self.addBoundOption(x); 105 | } 106 | }); 107 | } 108 | 109 | public getUsage () { 110 | let str = ''; 111 | this.args.forEach(function (x) { 112 | if (!x.positional) { 113 | str += '[-' + x.shortName + '|--' + x.longName + (x.type === 'boolean' ? '' : ' ' + x.type[0].toUpperCase()) + ']'; 114 | } else { 115 | str += x.longName; 116 | } 117 | str += ' '; 118 | }); 119 | return str; 120 | } 121 | 122 | public parse (args) { 123 | const nonPositionalArgumentMap = {}; 124 | const positionalArgumentList = []; 125 | this.args.forEach(function (x) { 126 | if (x.positional) { 127 | positionalArgumentList.push(x); 128 | } else { 129 | nonPositionalArgumentMap['-' + x.shortName] = x; 130 | nonPositionalArgumentMap['--' + x.longName] = x; 131 | } 132 | }); 133 | 134 | let leftoverArguments = []; 135 | 136 | while (args.length) { 137 | const argString = args.shift(); 138 | let argument = null, value = argString; 139 | if (argString == '--') { 140 | leftoverArguments = leftoverArguments.concat(args); 141 | break; 142 | } else if (argString.slice(0, 1) == '-' || argString.slice(0, 2) == '--') { 143 | argument = nonPositionalArgumentMap[argString]; 144 | // release || assert(argument, "Argument " + argString + " is unknown."); 145 | if (!argument) { 146 | continue; 147 | } 148 | if (argument.type !== 'boolean') { 149 | value = args.shift(); 150 | release || assert(value !== '-' && value !== '--', 'Argument ' + argString + ' must have a value.'); 151 | } else { 152 | if (args.length && ['yes', 'no', 'true', 'false', 't', 'f'].indexOf(args[0]) >= 0) { 153 | value = ['yes', 'true', 't'].indexOf(args.shift()) >= 0; 154 | } else { 155 | value = true; 156 | } 157 | } 158 | } else if (positionalArgumentList.length) { 159 | argument = positionalArgumentList.shift(); 160 | } else { 161 | leftoverArguments.push(value); 162 | } 163 | if (argument) { 164 | argument.parse(value); 165 | } 166 | } 167 | release || assert(positionalArgumentList.length === 0, 'Missing positional arguments.'); 168 | return leftoverArguments; 169 | } 170 | } 171 | 172 | export class OptionSet { 173 | name: string; 174 | settings: any; 175 | options: any; 176 | open: boolean = false; 177 | 178 | public static isOptionSet(obj: any): boolean { 179 | // We will be getting options from different iframe, so this function will 180 | // check if the obj somewhat like OptionSet. 181 | if (obj instanceof OptionSet) { 182 | return true; 183 | } 184 | if (typeof obj !== 'object' || obj === null || 185 | obj instanceof Option) { 186 | return false; 187 | } 188 | return ('options' in obj) && ('name' in obj) && ('settings' in obj); 189 | } 190 | 191 | constructor(name: string, settings: any = null) { 192 | this.name = name; 193 | this.settings = settings || {}; 194 | this.options = []; 195 | } 196 | 197 | public register(option) { 198 | if (OptionSet.isOptionSet(option)) { 199 | // check for duplicate option sets (bail if found) 200 | for (let i = 0; i < this.options.length; i++) { 201 | const optionSet = this.options[i]; 202 | if (OptionSet.isOptionSet(optionSet) && optionSet.name === option.name) { 203 | return optionSet; 204 | } 205 | } 206 | } 207 | this.options.push(option); 208 | if (this.settings) { 209 | if (OptionSet.isOptionSet(option)) { 210 | const optionSettings = this.settings[option.name]; 211 | if (isObject(optionSettings)) { 212 | option.settings = optionSettings.settings; 213 | option.open = optionSettings.open; 214 | } 215 | } else { 216 | // build_bundle chokes on this: 217 | // if (!isNullOrUndefined(this.settings[option.longName])) { 218 | if (typeof this.settings[option.longName] !== 'undefined') { 219 | switch (option.type) { 220 | case 'boolean': 221 | option.value = !!this.settings[option.longName]; 222 | break; 223 | case 'number': 224 | option.value = +this.settings[option.longName]; 225 | break; 226 | default: 227 | option.value = this.settings[option.longName]; 228 | break; 229 | } 230 | } 231 | } 232 | } 233 | return option; 234 | } 235 | 236 | public trace(writer) { 237 | writer.enter(this.name + ' {'); 238 | this.options.forEach(function (option) { 239 | option.trace(writer); 240 | }); 241 | writer.leave('}'); 242 | } 243 | 244 | public getSettings() { 245 | const settings = {}; 246 | this.options.forEach(function(option) { 247 | if (OptionSet.isOptionSet(option)) { 248 | settings[option.name] = { 249 | settings: option.getSettings(), 250 | open: option.open 251 | }; 252 | } else { 253 | settings[option.longName] = option.value; 254 | } 255 | }); 256 | return settings; 257 | } 258 | 259 | public setSettings(settings: any) { 260 | if (!settings) { 261 | return; 262 | } 263 | this.options.forEach(function (option) { 264 | if (OptionSet.isOptionSet(option)) { 265 | if (option.name in settings) { 266 | option.setSettings(settings[option.name].settings); 267 | } 268 | } else { 269 | if (option.longName in settings) { 270 | option.value = settings[option.longName]; 271 | } 272 | } 273 | }); 274 | } 275 | } 276 | 277 | export class Option { 278 | longName: string; 279 | shortName: string; 280 | type: string; 281 | defaultValue: any; 282 | value: any; // during options merge can be changed to accessor 283 | description: string; 284 | config: any; 285 | /** 286 | * Dat GUI control. 287 | */ 288 | // TODO remove, player will not have access to the DOM 289 | ctrl: any; 290 | // config: 291 | // { range: { min: 1, max: 5, step: 1 } } 292 | // { list: [ "item 1", "item 2", "item 3" ] } 293 | // { choices: { "choice 1": 1, "choice 2": 2, "choice 3": 3 } } 294 | constructor(shortName, longName, type, defaultValue, description, config = null) { 295 | this.longName = longName; 296 | this.shortName = shortName; 297 | this.type = type; 298 | this.defaultValue = defaultValue; 299 | this.value = defaultValue; 300 | this.description = description; 301 | this.config = config; 302 | } 303 | 304 | public parse (value) { 305 | this.value = value; 306 | } 307 | 308 | public trace (writer) { 309 | writer.writeLn(('-' + this.shortName + '|--' + this.longName).padRight(' ', 30) + 310 | ' = ' + this.type + ' ' + this.value + ' [' + this.defaultValue + ']' + 311 | ' (' + this.description + ')'); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /lib/factories/base/remoting.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { ShumwayCom } from './external'; 17 | import { ImageType } from './utilities'; 18 | import { DataBuffer } from '@awayjs/graphics'; 19 | import { notImplemented } from './utilities/Debug'; 20 | 21 | export interface IRemotable { 22 | _id: number; 23 | } 24 | 25 | /** 26 | * Remoting phases. 27 | */ 28 | export const enum RemotingPhase { 29 | /** 30 | * Objects are serialized. During this phase all reachable remotable objects (all objects 31 | * reachable from a root set) that are dirty are remoted. This includes all dirty object 32 | * properties except for dirty references. 33 | */ 34 | Objects, 35 | 36 | /** 37 | * Object references are serialized. All objects that are referred to have already been 38 | * remoted at this point. 39 | */ 40 | References 41 | } 42 | 43 | export const enum MessageBits { 44 | HasMatrix = 0x0001, 45 | HasBounds = 0x0002, 46 | HasChildren = 0x0004, 47 | HasColorTransform = 0x0008, 48 | HasClipRect = 0x0010, 49 | HasMiscellaneousProperties = 0x0020, 50 | HasMask = 0x0040, 51 | HasClip = 0x0080 52 | } 53 | 54 | export const enum IDMask { 55 | None = 0x00000000, 56 | Asset = 0x08000000 57 | } 58 | 59 | /** 60 | * Serialization Format. All commands start with a message tag. 61 | */ 62 | export const enum MessageTag { 63 | EOF = 0, 64 | 65 | /** 66 | * id int32, 67 | * hasBits int32, 68 | * matrix Matrix, 69 | * colorMatrix ColorMatrix, 70 | * mask int32, 71 | * misc 72 | * blendMode int32, 73 | * visible int32 74 | * 75 | * @type {number} 76 | */ 77 | UpdateFrame = 100, 78 | UpdateGraphics = 101, 79 | UpdateBitmapData = 102, 80 | UpdateTextContent = 103, 81 | UpdateStage = 104, 82 | UpdateNetStream = 105, 83 | RequestBitmapData = 106, 84 | UpdateCurrentMouseTarget = 107, 85 | 86 | DrawToBitmap = 200, 87 | 88 | MouseEvent = 300, 89 | KeyboardEvent = 301, 90 | FocusEvent = 302 91 | } 92 | 93 | export enum FilterType { 94 | Blur, 95 | DropShadow, 96 | ColorMatrix 97 | } 98 | 99 | /** 100 | * Dictates how color transforms are encoded. The majority of color transforms are 101 | * either identity or only modify the alpha multiplier, so we can encode these more 102 | * efficiently. 103 | */ 104 | export const enum ColorTransformEncoding { 105 | /** 106 | * Identity, no need to serialize all the fields. 107 | */ 108 | Identity = 0, 109 | 110 | /** 111 | * Identity w/ AlphaMultiplier, only the alpha multiplier is serialized. 112 | */ 113 | AlphaMultiplierOnly = 1, 114 | 115 | /** 116 | * Offsets w/ AlphaMultiplier. 117 | */ 118 | AlphaMultiplierWithOffsets = 2, 119 | 120 | /** 121 | * All fields are serialized. 122 | */ 123 | All = 3 124 | } 125 | 126 | /** 127 | * Dictates how matrices are encoded. 128 | */ 129 | export const enum MatrixEncoding { 130 | /** 131 | * Translation only. 132 | */ 133 | TranslationOnly = 0, 134 | 135 | /** 136 | * Scale and translation only. 137 | */ 138 | ScaleAndTranslationOnly = 1, 139 | 140 | /** 141 | * Uniform scale in the x and y direction and translation only. 142 | */ 143 | UniformScaleAndTranslationOnly = 2, 144 | 145 | /** 146 | * All fields are serialized. 147 | */ 148 | All = 3 149 | } 150 | 151 | export const enum VideoPlaybackEvent { 152 | Initialized = 0, 153 | Metadata = 1, 154 | PlayStart = 2, 155 | PlayStop = 3, 156 | BufferEmpty = 4, 157 | BufferFull = 5, 158 | Pause = 6, 159 | Unpause = 7, 160 | Seeking = 8, 161 | Seeked = 9, 162 | Progress = 10, 163 | Error = 11, 164 | } 165 | 166 | export const enum VideoControlEvent { 167 | Init = 1, 168 | Pause = 2, 169 | Seek = 3, 170 | GetTime = 4, 171 | GetBufferLength = 5, 172 | SetSoundLevels = 6, 173 | GetBytesLoaded = 7, 174 | GetBytesTotal = 8, 175 | EnsurePlaying = 9, 176 | } 177 | 178 | export const enum StageScaleMode { 179 | ShowAll = 0, 180 | ExactFit = 1, 181 | NoBorder = 2, 182 | NoScale = 4 183 | } 184 | 185 | export const enum StageAlignFlags { 186 | None = 0, 187 | Top = 1, 188 | Bottom = 2, 189 | Left = 4, 190 | Right = 8, 191 | 192 | TopLeft = Top | Left, 193 | BottomLeft = Bottom | Left, 194 | BottomRight = Bottom | Right, 195 | TopRight = Top | Right 196 | } 197 | 198 | export var MouseEventNames: string[] = [ 199 | 'click', 200 | 'dblclick', 201 | 'mousedown', 202 | 'mousemove', 203 | 'mouseup', 204 | 'mouseover', 205 | 'mouseout' 206 | ]; 207 | 208 | export var KeyboardEventNames: string[] = [ 209 | 'keydown', 210 | 'keypress', 211 | 'keyup' 212 | ]; 213 | 214 | export const enum KeyboardEventFlags { 215 | CtrlKey = 0x0001, 216 | AltKey = 0x0002, 217 | ShiftKey = 0x0004 218 | } 219 | 220 | export const enum FocusEventType { 221 | DocumentHidden, 222 | DocumentVisible, 223 | WindowBlur, 224 | WindowFocus 225 | } 226 | 227 | export interface DisplayParameters { 228 | stageWidth: number; 229 | stageHeight: number; 230 | pixelRatio: number; 231 | screenWidth: number; 232 | screenHeight: number; 233 | } 234 | 235 | export interface IGFXServiceObserver { 236 | displayParameters(displayParameters: DisplayParameters); 237 | focusEvent(data: any); 238 | keyboardEvent(data: any); 239 | mouseEvent(data: any); 240 | videoEvent(id: number, eventType: VideoPlaybackEvent, data: any); 241 | } 242 | 243 | export interface IGFXService { 244 | addObserver(observer: IGFXServiceObserver); 245 | removeObserver(observer: IGFXServiceObserver); 246 | 247 | update(updates: DataBuffer, assets: Array): void; 248 | updateAndGet(updates: DataBuffer, assets: Array): any; 249 | frame(): void; 250 | videoControl(id: number, eventType: VideoControlEvent, data: any): any; 251 | registerFont(syncId: number, data: Uint8Array): Promise; 252 | registerImage(syncId: number, symbolId: number, imageType: ImageType, 253 | data: Uint8Array, alphaData: Uint8Array): Promise; 254 | fscommand(command: string, args: string): void; 255 | } 256 | 257 | /** 258 | * Messaging peer for sending data synchronously and asynchronously. Currently 259 | * used by GFX and Player iframes. 260 | */ 261 | export interface ITransportPeer { 262 | onAsyncMessage: (msg: any) => void; 263 | onSyncMessage: (msg: any) => any; 264 | 265 | postAsyncMessage(msg: any, transfers?: any[]): void; 266 | sendSyncMessage(msg: any, transfers?: any[]): any; 267 | } 268 | 269 | /** 270 | * Implementation of ITransportPeer that uses standard DOM postMessage and 271 | * events to exchange data between messaging peers. 272 | */ 273 | export class WindowTransportPeer implements ITransportPeer { 274 | set onAsyncMessage(callback: (msg: any) => void) { 275 | this.window.addEventListener('message', function (e) { 276 | Promise.resolve(e.data).then(function (msg) { // delay 277 | callback(msg); 278 | }); 279 | }); 280 | } 281 | 282 | set onSyncMessage(callback: (msg: any) => any) { 283 | this.window.addEventListener('syncmessage', function (e) { 284 | const wrappedMessage = (e).detail; 285 | wrappedMessage.result = callback(wrappedMessage.msg); 286 | }); 287 | } 288 | 289 | constructor(public window: Window, public target: Window) { 290 | // 291 | } 292 | 293 | postAsyncMessage(msg: any, transfers?: any[]): void { 294 | this.target.postMessage(msg, '*', transfers); 295 | } 296 | 297 | sendSyncMessage(msg: any, transfers?: any[]): any { 298 | const event = this.target.document.createEvent('CustomEvent'); 299 | const wrappedMessage = { 300 | msg: msg, 301 | result: undefined 302 | }; 303 | event.initCustomEvent('syncmessage', false, false, wrappedMessage); 304 | this.target.dispatchEvent(event); 305 | return wrappedMessage.result; 306 | } 307 | } 308 | 309 | /** 310 | * Implementation of ITransportPeer that uses ShumwayCom API to exchange data 311 | * between messaging peers. 312 | */ 313 | export class ShumwayComTransportPeer implements ITransportPeer { 314 | set onAsyncMessage(callback: (msg: any) => void) { 315 | notImplemented('ShumwayComTransportPeer.onAsyncMessage not implemented yet'); 316 | //ShumwayCom.setAsyncMessageCallback(callback); 317 | } 318 | 319 | set onSyncMessage(callback: (msg: any) => any) { 320 | notImplemented('ShumwayComTransportPeer.onSyncMessage not implemented yet'); 321 | //ShumwayCom.setSyncMessageCallback(callback); 322 | } 323 | 324 | postAsyncMessage(msg: any, transfers?: any[]): void { 325 | notImplemented('ShumwayComTransportPeer.postAsyncMessage not implemented yet'); 326 | //ShumwayCom.postAsyncMessage(msg); 327 | } 328 | 329 | sendSyncMessage(msg: any, transfers?: any[]): any { 330 | notImplemented('ShumwayComTransportPeer.sendSyncMessage not implemented yet'); 331 | return null;//ShumwayCom.sendSyncMessage(msg); 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /lib/factories/base/settings.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Option, OptionSet } from './options'; 18 | 19 | export var ROOT: string = 'Shumway Options'; 20 | export var shumwayOptions = new OptionSet(ROOT); 21 | 22 | export function setSettings(settings: any) { 23 | shumwayOptions.setSettings(settings); 24 | } 25 | 26 | export function getSettings() { 27 | return shumwayOptions.getSettings(); 28 | } 29 | 30 | export var loggingOptions = shumwayOptions.register(new OptionSet('Logging Options')); 31 | 32 | export var omitRepeatedWarnings = loggingOptions.register(new Option('wo', 'warnOnce', 'boolean', true, 'Omit Repeated Warnings')); 33 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/ArrayUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | import { nearestPowerOfTwo } from './IntegerUtilities'; 3 | import { assert, release } from './Debug'; 4 | 5 | /** 6 | * Pops elements from a source array into a destination array. This avoids 7 | * allocations and should be faster. The elements in the destination array 8 | * are pushed in the same order as they appear in the source array: 9 | * 10 | * popManyInto([1, 2, 3], 2, dst) => dst = [2, 3] 11 | */ 12 | export function popManyInto(src: any [], count: number, dst: any []) { 13 | release || assert(src.length >= count); 14 | for (let i = count - 1; i >= 0; i--) { 15 | dst[i] = src.pop(); 16 | } 17 | dst.length = count; 18 | } 19 | 20 | export function popMany(array: T [], count: number): T [] { 21 | release || assert(array.length >= count); 22 | const start = array.length - count; 23 | const result = array.slice(start, this.length); 24 | array.length = start; 25 | return result; 26 | } 27 | 28 | /** 29 | * Just deletes several array elements from the end of the list. 30 | */ 31 | export function popManyIntoVoid(array: any [], count: number) { 32 | release || assert(array.length >= count); 33 | array.length = array.length - count; 34 | } 35 | 36 | export function pushMany(dst: any [], src: any []) { 37 | for (let i = 0; i < src.length; i++) { 38 | dst.push(src[i]); 39 | } 40 | } 41 | 42 | export function top(array: any []) { 43 | return array.length && array[array.length - 1]; 44 | } 45 | 46 | export function last(array: any []) { 47 | return array.length && array[array.length - 1]; 48 | } 49 | 50 | export function peek(array: any []) { 51 | release || assert(array.length > 0); 52 | return array[array.length - 1]; 53 | } 54 | 55 | export function indexOf(array: T [], value: T): number { 56 | for (let i = 0, j = array.length; i < j; i++) { 57 | if (array[i] === value) { 58 | return i; 59 | } 60 | } 61 | return -1; 62 | } 63 | 64 | export function equals(a: T [], b: T []): boolean { 65 | if (a.length !== b.length) { 66 | return false; 67 | } 68 | for (let i = 0; i < a.length; i++) { 69 | if (a[i] !== b[i]) { 70 | return false; 71 | } 72 | } 73 | return true; 74 | } 75 | 76 | export function pushUnique(array: T [], value: T): number { 77 | for (let i = 0, j = array.length; i < j; i++) { 78 | if (array[i] === value) { 79 | return i; 80 | } 81 | } 82 | array.push(value); 83 | return array.length - 1; 84 | } 85 | 86 | export function unique(array: T []): T [] { 87 | const result = []; 88 | for (let i = 0; i < array.length; i++) { 89 | pushUnique(result, array[i]); 90 | } 91 | return result; 92 | } 93 | 94 | export function copyFrom(dst: any [], src: any []) { 95 | dst.length = 0; 96 | pushMany(dst, src); 97 | } 98 | 99 | /** 100 | * Makes sure that a typed array has the requested capacity. If required, it creates a new 101 | * instance of the array's class with a power-of-two capacity at least as large as required. 102 | */ 103 | export function ensureTypedArrayCapacity(array: T, capacity: number): T { 104 | if (array.length < capacity) { 105 | const oldArray = array; 106 | array = new (array).constructor(nearestPowerOfTwo(capacity)); 107 | array.set(oldArray, 0); 108 | } 109 | return array; 110 | } 111 | 112 | export function memCopy(destination: T, source: T, doffset: number = 0, 113 | soffset: number = 0, length: number = 0) { 114 | if (soffset > 0 || (length > 0 && length < source.length)) { 115 | if (length <= 0) { 116 | length = source.length - soffset; 117 | } 118 | destination.set(source.subarray(soffset, soffset + length), doffset); 119 | } else { 120 | destination.set(source, doffset); 121 | } 122 | } 123 | 124 | export interface TypedArray { 125 | buffer: ArrayBuffer; 126 | length: number; 127 | set: (array: TypedArray, offset?: number) => void; 128 | subarray: (begin: number, end?: number) => TypedArray; 129 | } 130 | 131 | export interface IDataDecoder { 132 | onData: (data: Uint8Array) => void; 133 | onError: (e) => void; 134 | push(data: Uint8Array); 135 | close(); 136 | } 137 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/Cache.ts: -------------------------------------------------------------------------------- 1 | import { assert, release } from './Debug'; 2 | 3 | /** 4 | * An extremely naive cache with a maximum size. 5 | * TODO: LRU 6 | */ 7 | export class Cache { 8 | private _data; 9 | private _size: number; 10 | private _maxSize: number; 11 | constructor(maxSize: number) { 12 | this._data = Object.create(null); 13 | this._size = 0; 14 | this._maxSize = maxSize; 15 | } 16 | 17 | get(key) { 18 | return this._data[key]; 19 | } 20 | 21 | set(key, value) { 22 | release || assert(!(key in this._data)); // Cannot mutate cache entries. 23 | if (this._size >= this._maxSize) { 24 | return false; 25 | } 26 | this._data[key] = value; 27 | this._size++; 28 | return true; 29 | } 30 | } -------------------------------------------------------------------------------- /lib/factories/base/utilities/ClipboardService.ts: -------------------------------------------------------------------------------- 1 | 2 | import { notImplemented } from './Debug'; 3 | 4 | export interface IClipboardService { 5 | setClipboard(data: string): void; 6 | } 7 | 8 | export var ClipboardService: IClipboardService = { 9 | setClipboard(data: string) { notImplemented('setClipboard'); } 10 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/ColorUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | import { concat9 } from './StringUtilities'; 3 | import { Cache } from './Cache'; 4 | import { ImageType } from '../utilities'; 5 | import { assert, release, somewhatImplemented } from './Debug'; 6 | import { swap32 } from './IntegerUtilities'; 7 | 8 | export function RGBAToARGB(rgba: number): number { 9 | return ((rgba >> 8) & 0x00ffffff) | ((rgba & 0xff) << 24); 10 | } 11 | 12 | export function ARGBToRGBA(argb: number): number { 13 | return argb << 8 | ((argb >> 24) & 0xff); 14 | } 15 | 16 | /** 17 | * Cache frequently used rgba -> css style conversions. 18 | */ 19 | const rgbaToCSSStyleCache = new Cache(1024); 20 | 21 | export function rgbaToCSSStyle(rgba: number): string { 22 | let result = rgbaToCSSStyleCache.get(rgba); 23 | if (typeof result === 'string') { 24 | return result; 25 | } 26 | result = concat9('rgba(', rgba >> 24 & 0xff, ',', rgba >> 16 & 0xff, ',', rgba >> 8 & 0xff, ',', (rgba & 0xff) / 0xff, ')'); 27 | rgbaToCSSStyleCache.set(rgba, result); 28 | return result; 29 | } 30 | 31 | /** 32 | * Cache frequently used css -> rgba styles conversions. 33 | */ 34 | const cssStyleToRGBACache = new Cache(1024); 35 | 36 | export function cssStyleToRGBA(style: string) { 37 | let result = cssStyleToRGBACache.get(style); 38 | if (typeof result === 'number') { 39 | return result; 40 | } 41 | result = 0xff0000ff; // Red 42 | if (style[0] === '#') { 43 | if (style.length === 7) { 44 | result = (parseInt(style.substring(1), 16) << 8) | 0xff; 45 | } 46 | } else if (style[0] === 'r') { 47 | // We don't parse all types of rgba(....) color styles. We only handle the 48 | // ones we generate ourselves. 49 | const values = style.substring(5, style.length - 1).split(','); 50 | const r = parseInt(values[0]); 51 | const g = parseInt(values[1]); 52 | const b = parseInt(values[2]); 53 | const a = parseFloat(values[3]); 54 | result = (r & 0xff) << 24 | 55 | (g & 0xff) << 16 | 56 | (b & 0xff) << 8 | 57 | ((a * 255) & 0xff); 58 | } 59 | cssStyleToRGBACache.set(style, result); 60 | return result; 61 | } 62 | 63 | export function hexToRGB(color: string): number { 64 | return parseInt(color.slice(1), 16); 65 | } 66 | 67 | export function rgbToHex(color: number): string { 68 | return '#' + ('000000' + (color >>> 0).toString(16)).slice(-6); 69 | } 70 | 71 | export function isValidHexColor(value: any): boolean { 72 | return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value); 73 | } 74 | 75 | export function clampByte(value: number) { 76 | return Math.max(0, Math.min(255, value)); 77 | } 78 | 79 | /** 80 | * Unpremultiplies the given |pARGB| color value. 81 | */ 82 | export function unpremultiplyARGB(pARGB: number) { 83 | let b = (pARGB >> 0) & 0xff; 84 | let g = (pARGB >> 8) & 0xff; 85 | let r = (pARGB >> 16) & 0xff; 86 | const a = (pARGB >> 24) & 0xff; 87 | r = Math.imul(255, r) / a & 0xff; 88 | g = Math.imul(255, g) / a & 0xff; 89 | b = Math.imul(255, b) / a & 0xff; 90 | return a << 24 | r << 16 | g << 8 | b; 91 | } 92 | 93 | /** 94 | * Premultiplies the given |pARGB| color value. 95 | */ 96 | export function premultiplyARGB(uARGB: number) { 97 | let b = (uARGB >> 0) & 0xff; 98 | let g = (uARGB >> 8) & 0xff; 99 | let r = (uARGB >> 16) & 0xff; 100 | const a = (uARGB >> 24) & 0xff; 101 | r = ((Math.imul(r, a) + 127) / 255) | 0; 102 | g = ((Math.imul(g, a) + 127) / 255) | 0; 103 | b = ((Math.imul(b, a) + 127) / 255) | 0; 104 | return a << 24 | r << 16 | g << 8 | b; 105 | } 106 | 107 | let premultiplyTable: Uint8Array; 108 | 109 | /** 110 | * All possible alpha values and colors 256 * 256 = 65536 entries. Experiments 111 | * indicate that doing unpremultiplication this way is roughly 5x faster. 112 | * 113 | * To lookup a color |c| in the table at a given alpha value |a| use: 114 | * |(a << 8) + c| to compute the index. This layout order was chosen to make 115 | * table lookups cache friendly, it actually makes a difference. 116 | * 117 | * TODO: Figure out if memory / speed tradeoff is worth it. 118 | */ 119 | let unpremultiplyTable: Uint8Array; 120 | 121 | /** 122 | * Make sure to call this before using the |unpremultiplyARGBUsingTableLookup| or 123 | * |premultiplyARGBUsingTableLookup| functions. We want to execute this lazily so 124 | * we don't incur any startup overhead. 125 | */ 126 | export function ensureUnpremultiplyTable() { 127 | if (!unpremultiplyTable) { 128 | unpremultiplyTable = new Uint8Array(256 * 256); 129 | for (let c = 0; c < 256; c++) { 130 | for (let a = 0; a < 256; a++) { 131 | unpremultiplyTable[(a << 8) + c] = Math.imul(255, c) / a; 132 | } 133 | } 134 | } 135 | } 136 | 137 | export function getUnpremultiplyTable(): Uint8Array { 138 | ensureUnpremultiplyTable(); 139 | return unpremultiplyTable; 140 | } 141 | 142 | export function tableLookupUnpremultiplyARGB(pARGB): number { 143 | pARGB = pARGB | 0; 144 | const a = (pARGB >> 24) & 0xff; 145 | if (a === 0) { 146 | return 0; 147 | } else if (a === 0xff) { 148 | return pARGB; 149 | } 150 | let b = (pARGB >> 0) & 0xff; 151 | let g = (pARGB >> 8) & 0xff; 152 | let r = (pARGB >> 16) & 0xff; 153 | const o = a << 8; 154 | const T = unpremultiplyTable; 155 | r = T[o + r]; 156 | g = T[o + g]; 157 | b = T[o + b]; 158 | return a << 24 | r << 16 | g << 8 | b; 159 | } 160 | 161 | /** 162 | * The blending equation for unpremultiplied alpha is: 163 | * 164 | * (src.rgb * src.a) + (dst.rgb * (1 - src.a)) 165 | * 166 | * For premultiplied alpha src.rgb and dst.rgb are already 167 | * premultiplied by alpha, so the equation becomes: 168 | * 169 | * src.rgb + (dst.rgb * (1 - src.a)) 170 | * 171 | * TODO: Not sure what to do about the dst.rgb which is 172 | * premultiplied by its alpah, but this appears to work. 173 | * 174 | * We use the "double blend trick" (http://stereopsis.com/doubleblend.html) to 175 | * compute GA and BR without unpacking them. 176 | */ 177 | declare interface Math{ 178 | imul(a,b): number; 179 | } 180 | 181 | export function blendPremultipliedBGRA(tpBGRA: number, spBGRA: number) { 182 | const sA = spBGRA & 0xff; 183 | const sGA = spBGRA & 0x00ff00ff; 184 | const sBR = spBGRA >> 8 & 0x00ff00ff; 185 | let tGA = tpBGRA & 0x00ff00ff; 186 | let tBR = tpBGRA >> 8 & 0x00ff00ff; 187 | const A = 256 - sA; 188 | tGA = Math.imul(tGA, A) >> 8; 189 | tBR = Math.imul(tBR, A) >> 8; 190 | return ((sBR + tBR & 0x00ff00ff) << 8) | (sGA + tGA & 0x00ff00ff); 191 | } 192 | 193 | export function convertImage(sourceFormat: ImageType, targetFormat: ImageType, source: Int32Array, target: Int32Array) { 194 | if (source !== target) { 195 | release || assert(source.buffer !== target.buffer, 'Can\'t handle overlapping views.'); 196 | } 197 | const length = source.length; 198 | if (sourceFormat === targetFormat) { 199 | if (source === target) { 200 | return; 201 | } 202 | for (var i = 0; i < length; i++) { 203 | target[i] = source[i]; 204 | } 205 | return; 206 | } 207 | // enterTimeline("convertImage", ImageType[sourceFormat] + " to " + ImageType[targetFormat] + " (" + memorySizeToString(source.length)); 208 | if (sourceFormat === ImageType.PremultipliedAlphaARGB && 209 | targetFormat === ImageType.StraightAlphaRGBA) { 210 | ensureUnpremultiplyTable(); 211 | for (var i = 0; i < length; i++) { 212 | const pBGRA = source[i]; 213 | const a = pBGRA & 0xff; 214 | if (a === 0) { 215 | target[i] = 0; 216 | } else if (a === 0xff) { 217 | target[i] = 0xff000000 | ((pBGRA >> 8) & 0x00ffffff); 218 | } else { 219 | let b = (pBGRA >> 24) & 0xff; 220 | let g = (pBGRA >> 16) & 0xff; 221 | let r = (pBGRA >> 8) & 0xff; 222 | const o = a << 8; 223 | const T = unpremultiplyTable; 224 | r = T[o + r]; 225 | g = T[o + g]; 226 | b = T[o + b]; 227 | target[i] = a << 24 | b << 16 | g << 8 | r; 228 | } 229 | } 230 | } else if (sourceFormat === ImageType.StraightAlphaARGB && 231 | targetFormat === ImageType.StraightAlphaRGBA) { 232 | for (var i = 0; i < length; i++) { 233 | target[i] = swap32(source[i]); 234 | } 235 | } else if (sourceFormat === ImageType.StraightAlphaRGBA && 236 | targetFormat === ImageType.PremultipliedAlphaARGB) { 237 | for (var i = 0; i < length; i++) { 238 | const uABGR = source[i]; 239 | const uARGB = (uABGR & 0xFF00FF00) | // A_G_ 240 | (uABGR >> 16) & 0xff | // A_GB 241 | (uABGR & 0xff) << 16; // ARGR 242 | target[i] = swap32(premultiplyARGB(uARGB)); 243 | } 244 | } else { 245 | release || somewhatImplemented('Image Format Conversion: ' + ImageType[sourceFormat] + ' -> ' + ImageType[targetFormat]); 246 | // Copy the buffer over for now, we should at least get some image output. 247 | for (var i = 0; i < length; i++) { 248 | target[i] = source[i]; 249 | } 250 | } 251 | // leaveTimeline("convertImage"); 252 | } 253 | 254 | export const ColorUtilitites = { 255 | RGBAToARGB:RGBAToARGB, 256 | ARGBToRGBA:ARGBToRGBA, 257 | cssStyleToRGBACache:cssStyleToRGBACache, 258 | rgbaToCSSStyle:rgbaToCSSStyle, 259 | cssStyleToRGBA:cssStyleToRGBA, 260 | hexToRGB:hexToRGB, 261 | rgbToHex:rgbToHex, 262 | isValidHexColor:isValidHexColor, 263 | clampByte:clampByte, 264 | unpremultiplyARGB:unpremultiplyARGB, 265 | premultiplyARGB:premultiplyARGB, 266 | premultiplyTable:premultiplyTable, 267 | unpremultiplyTable:unpremultiplyTable, 268 | ensureUnpremultiplyTable:ensureUnpremultiplyTable, 269 | getUnpremultiplyTable:getUnpremultiplyTable, 270 | tableLookupUnpremultiplyARGB:tableLookupUnpremultiplyARGB, 271 | blendPremultipliedBGRA:blendPremultipliedBGRA, 272 | convertImage:convertImage 273 | }; 274 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/Debug.ts: -------------------------------------------------------------------------------- 1 | 2 | import { argumentsToString } from '../utilities'; 3 | import { omitRepeatedWarnings } from '../settings'; 4 | 5 | export var release: boolean = true; 6 | export var debugable: boolean = true; 7 | 8 | export class Debug { 9 | public static release: boolean=release; 10 | public static assert=assert; 11 | public static error=error; 12 | public static assertUnreachable=assertUnreachable; 13 | public static assertNotImplemented=assertNotImplemented; 14 | public static warning=warning; 15 | public static notImplemented=notImplemented; 16 | public static dummyConstructor=dummyConstructor; 17 | public static warnCounts=warnCounts; 18 | public static abstractMethod=abstractMethod; 19 | public static somewhatImplemented=somewhatImplemented; 20 | public static unexpected=unexpected; 21 | public static unexpectedCase=unexpectedCase; 22 | } 23 | 24 | export function notImplemented(message: string) { 25 | warning('AVM1 not implemented: ' + message); 26 | } 27 | 28 | export function somewhatImplemented(message: string) { 29 | warning('AVM1 somewhatImplemented: ' + message); 30 | } 31 | 32 | export function error(message: string) { 33 | console.error(message); 34 | throw new Error(message); 35 | } 36 | 37 | export function assert(condition: any, message: any = 'assertion failed') { 38 | if (condition === '') { // avoid inadvertent false positive 39 | condition = true; 40 | } 41 | if (!condition) { 42 | if (typeof console !== 'undefined' && 'assert' in console) { 43 | console.assert(false, message); 44 | throw new Error(message); 45 | } else { 46 | error(message.toString()); 47 | } 48 | } 49 | } 50 | 51 | export function assertUnreachable(msg: string): void { 52 | const location = new Error().stack.split('\n')[1]; 53 | throw new Error('Reached unreachable location ' + location + msg); 54 | } 55 | 56 | export function assertNotImplemented(condition: boolean, message: string) { 57 | if (!condition) { 58 | error('notImplemented: ' + message); 59 | } 60 | } 61 | 62 | const _warnedCounts = Object.create(null); 63 | 64 | export function warning(message: any, arg1?: any, arg2?: any/*...messages: any[]*/) { 65 | if (release) { 66 | return; 67 | } 68 | const key = argumentsToString(arguments); 69 | if (_warnedCounts[key]) { 70 | _warnedCounts[key]++; 71 | if (omitRepeatedWarnings.value) { 72 | return; 73 | } 74 | } 75 | _warnedCounts[key] = 1; 76 | console.warn.apply(console, arguments); 77 | } 78 | 79 | export function warnCounts() { 80 | const list = []; 81 | for (const key in _warnedCounts) { 82 | list.push({ key: key, count: _warnedCounts[key] }); 83 | } 84 | list.sort((entry, prev) => prev.count - entry.count); 85 | return list.reduce((result, entry) => (result += '\n' + entry.count + '\t' + entry.key), ''); 86 | } 87 | 88 | export function dummyConstructor(message: string) { 89 | release || assert(false, 'Dummy Constructor: ' + message); 90 | } 91 | 92 | export function abstractMethod(message: string) { 93 | release || assert(false, 'Abstract Method ' + message); 94 | } 95 | 96 | export function unexpected(message?: any) { 97 | assert(false, 'Unexpected: ' + message); 98 | } 99 | 100 | export function unexpectedCase(message?: any) { 101 | assert(false, 'Unexpected Case: ' + message); 102 | } 103 | 104 | interface IAwayDebug extends StringMap {} 105 | 106 | declare global { 107 | interface Window { _AWAY_DEBUG_: IAwayDebug; } 108 | } 109 | 110 | // export WRITER API for capture AVM2 reports 111 | 112 | type TMethodType = 'undefined' | 'object' | 'boolean' | 'number' | 'string' | 'function'; 113 | 114 | export interface IDebugMethodDeclaration { 115 | name: string; 116 | declaration?: Array<{name: string, type: TMethodType}>; 117 | description?: string; 118 | } 119 | 120 | export function registerDebugMethod(func: (...args: any[]) => any, methodInfo: IDebugMethodDeclaration) { 121 | if (!debugable) { 122 | return; 123 | } 124 | 125 | const name = methodInfo.name; 126 | delete methodInfo.name; 127 | 128 | if (!methodInfo || !name) { 129 | throw new Error('Method name should not be empty'); 130 | } 131 | 132 | const api: Object = window._AWAY_DEBUG_ = (window._AWAY_DEBUG_ || {}); 133 | 134 | if (api.hasOwnProperty(name)) { 135 | console.warn(`Overrides existed debug method definition: ${name}`); 136 | } 137 | 138 | Object.assign(func, methodInfo); 139 | 140 | api[name] = func; 141 | } 142 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/ExternalInterfaceService.ts: -------------------------------------------------------------------------------- 1 | const __flash__toXML = function __flash__toXML(obj) { 2 | let xml; 3 | switch (typeof obj) { 4 | case 'boolean': 5 | return obj ? '' : ''; 6 | case 'number': 7 | return '' + obj + ''; 8 | case 'object': 9 | if (obj === null) { 10 | return ''; 11 | } 12 | if ('hasOwnProperty' in obj && obj.hasOwnProperty('length')) { 13 | // array 14 | xml = ''; 15 | for (let i = 0; i < obj.length; i++) { 16 | xml += '' + __flash__toXML(obj[i]) + ''; 17 | } 18 | return xml + ''; 19 | } 20 | xml = ''; 21 | for (const key in obj) { 22 | xml += '' + __flash__toXML(obj[key]) + ''; 23 | } 24 | return xml + ''; 25 | case 'string': 26 | return '' 27 | + obj.replace(/&/g, '&').replace(//g, '>') 28 | + ''; 29 | case 'undefined': 30 | return ''; 31 | } 32 | }; 33 | export class ExternalInterfaceService { 34 | 35 | public static enabled: boolean = true; 36 | private static _interfaceID: string = 'flash'; 37 | public static callback: (functionName: string, args: any[]) => any; 38 | 39 | public static get interfaceID(): string { 40 | return ExternalInterfaceService._interfaceID; 41 | } 42 | 43 | public static set interfaceID(value: string) { 44 | ExternalInterfaceService._interfaceID = value; 45 | } 46 | 47 | public static ensureInit() { 48 | if (!window[ExternalInterfaceService._interfaceID]) 49 | window[ExternalInterfaceService._interfaceID] = {}; 50 | window[ExternalInterfaceService._interfaceID]['__flash__toXML'] = __flash__toXML; 51 | } 52 | 53 | public static initJS(callback: (functionName: string, args: any[]) => any) { 54 | ExternalInterfaceService.callback = callback; 55 | } 56 | 57 | public static registerCallback(functionName: string) { 58 | ExternalInterfaceService.ensureInit(); 59 | window[ExternalInterfaceService._interfaceID][functionName] = (...args)=>{ 60 | return ExternalInterfaceService.callback(functionName, args); 61 | }; 62 | } 63 | 64 | public static unregisterCallback(functionName: string) { 65 | ExternalInterfaceService.ensureInit(); 66 | delete window[ExternalInterfaceService._interfaceID][functionName]; 67 | } 68 | 69 | public static eval(expression: string): any { 70 | ExternalInterfaceService.ensureInit(); 71 | try { 72 | return window.eval(expression); 73 | } catch (e) { 74 | console.warn('[ExternalInterfaceService] Eval crashed:\n', expression, '\n' + e.message); 75 | } 76 | } 77 | 78 | public static call(request: string): any { 79 | // ... 80 | } 81 | 82 | public static getId(): string { return null; } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/FileLoadingService.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface FileLoadingRequest { 3 | url: string; 4 | data: any; 5 | } 6 | 7 | export interface FileLoadingProgress { 8 | bytesLoaded: number; 9 | bytesTotal: number; 10 | } 11 | 12 | export interface FileLoadingSession { 13 | onopen?: () => void; 14 | onclose?: () => void; 15 | onprogress?: (data: any, progressStatus: FileLoadingProgress) => void; 16 | onhttpstatus?: (location: string, httpStatus: number, httpHeaders: any) => void; 17 | onerror?: (e) => void; 18 | open(request: FileLoadingRequest); 19 | close: () => void; 20 | } 21 | 22 | export interface IFileLoadingService { 23 | createSession(): FileLoadingSession; 24 | resolveUrl(url: string): string; 25 | navigateTo(url: string, target: string); 26 | } 27 | 28 | export var FileLoadingService: IFileLoadingService; 29 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/FunctionUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | export function makeForwardingGetter(target: string): () => any { 3 | return <() => any> new Function('return this["' + target + '"]//# sourceURL=fwd-get-' + 4 | target + '.as'); 5 | } 6 | 7 | export function makeForwardingSetter(target: string): (any) => void { 8 | return <(any) => void> new Function('value', 'this["' + target + '"] = value;' + 9 | '//# sourceURL=fwd-set-' + target + '.as'); 10 | } 11 | 12 | export const FunctionUtilities = { 13 | makeForwardingGetter:makeForwardingGetter, 14 | makeForwardingSetter:makeForwardingSetter 15 | }; 16 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/HashUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | const _md5R = new Uint8Array([ 3 | 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 4 | 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5 | 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6 | 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]); 7 | 8 | const _md5K = new Int32Array([ 9 | -680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426, 10 | -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162, 11 | 1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632, 12 | 643717713, -373897302, -701558691, 38016083, -660478335, -405537848, 13 | 568446438, -1019803690, -187363961, 1163531501, -1444681467, -51403784, 14 | 1735328473, -1926607734, -378558, -2022574463, 1839030562, -35309556, 15 | -1530992060, 1272893353, -155497632, -1094730640, 681279174, -358537222, 16 | -722521979, 76029189, -640364487, -421815835, 530742520, -995338651, 17 | -198630844, 1126891415, -1416354905, -57434055, 1700485571, -1894986606, 18 | -1051523, -2054922799, 1873313359, -30611744, -1560198380, 1309151649, 19 | -145523070, -1120210379, 718787259, -343485551]); 20 | 21 | export function hashBytesTo32BitsMD5(data: Uint8Array, offset: number, length: number): number { 22 | return hashBytesTo128BitsMD5(data, offset, length)[0]; 23 | } 24 | 25 | export function hashBytesTo128BitsMD5(data: Uint8Array, offset: number, length: number): Uint32Array { 26 | const r = _md5R; 27 | const k = _md5K; 28 | let h0 = 1732584193, h1 = -271733879, h2 = -1732584194, h3 = 271733878; 29 | // pre-processing 30 | const paddedLength = (length + 72) & ~63; // data + 9 extra bytes 31 | const padded = new Uint8Array(paddedLength); 32 | let i, j, n; 33 | for (i = 0; i < length; ++i) { 34 | padded[i] = data[offset++]; 35 | } 36 | padded[i++] = 0x80; 37 | n = paddedLength - 8; 38 | while (i < n) { 39 | padded[i++] = 0; 40 | } 41 | padded[i++] = (length << 3) & 0xFF; 42 | padded[i++] = (length >> 5) & 0xFF; 43 | padded[i++] = (length >> 13) & 0xFF; 44 | padded[i++] = (length >> 21) & 0xFF; 45 | padded[i++] = (length >>> 29) & 0xFF; 46 | padded[i++] = 0; 47 | padded[i++] = 0; 48 | padded[i++] = 0; 49 | // chunking 50 | // TODO ArrayBuffer ? 51 | const w = new Int32Array(16); 52 | for (i = 0; i < paddedLength;) { 53 | for (j = 0; j < 16; ++j, i += 4) { 54 | w[j] = (padded[i] | (padded[i + 1] << 8) | 55 | (padded[i + 2] << 16) | (padded[i + 3] << 24)); 56 | } 57 | var a = h0, b = h1, c = h2, d = h3, f, g; 58 | for (j = 0; j < 64; ++j) { 59 | if (j < 16) { 60 | f = (b & c) | ((~b) & d); 61 | g = j; 62 | } else if (j < 32) { 63 | f = (d & b) | ((~d) & c); 64 | g = (5 * j + 1) & 15; 65 | } else if (j < 48) { 66 | f = b ^ c ^ d; 67 | g = (3 * j + 5) & 15; 68 | } else { 69 | f = c ^ (b | (~d)); 70 | g = (7 * j) & 15; 71 | } 72 | const tmp = d, rotateArg = (a + f + k[j] + w[g]) | 0, rotate = r[j]; 73 | d = c; 74 | c = b; 75 | b = (b + ((rotateArg << rotate) | (rotateArg >>> (32 - rotate)))) | 0; 76 | a = tmp; 77 | } 78 | h0 = (h0 + a) | 0; 79 | h1 = (h1 + b) | 0; 80 | h2 = (h2 + c) | 0; 81 | h3 = (h3 + d) | 0; 82 | } 83 | 84 | return new Uint32Array([h0, h1, h2, h3]); 85 | } 86 | 87 | export function mixHash(a: number, b: number) { 88 | return (((31 * a) | 0) + b) | 0; 89 | } 90 | 91 | export const HashUtilities = { 92 | _md5R, 93 | _md5K, 94 | hashBytesTo32BitsMD5, 95 | hashBytesTo128BitsMD5, 96 | mixHash 97 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/IntegerUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | const sharedBuffer = new ArrayBuffer(8); 3 | export var i8 = new Int8Array(sharedBuffer); 4 | export var u8 = new Uint8Array(sharedBuffer); 5 | export var i32 = new Int32Array(sharedBuffer); 6 | export var f32 = new Float32Array(sharedBuffer); 7 | export var f64 = new Float64Array(sharedBuffer); 8 | export var nativeLittleEndian = new Int8Array(new Int32Array([1]).buffer)[0] === 1; 9 | 10 | /** 11 | * Convert a float into 32 bits. 12 | */ 13 | export function floatToInt32(v: number) { 14 | f32[0] = v; return i32[0]; 15 | } 16 | 17 | /** 18 | * Convert 32 bits into a float. 19 | */ 20 | export function int32ToFloat(i: number) { 21 | i32[0] = i; return f32[0]; 22 | } 23 | 24 | /** 25 | * Swap the bytes of a 16 bit number. 26 | */ 27 | export function swap16(i: number) { 28 | return ((i & 0xFF) << 8) | ((i >> 8) & 0xFF); 29 | } 30 | 31 | /** 32 | * Swap the bytes of a 32 bit number. 33 | */ 34 | export function swap32(i: number) { 35 | return ((i & 0xFF) << 24) | ((i & 0xFF00) << 8) | ((i >> 8) & 0xFF00) | ((i >> 24) & 0xFF); 36 | } 37 | 38 | /** 39 | * Converts a number to s8.u8 fixed point representation. 40 | */ 41 | export function toS8U8(v: number) { 42 | return ((v * 256) << 16) >> 16; 43 | } 44 | 45 | /** 46 | * Converts a number from s8.u8 fixed point representation. 47 | */ 48 | export function fromS8U8(i: number) { 49 | return i / 256; 50 | } 51 | 52 | /** 53 | * Round trips a number through s8.u8 conversion. 54 | */ 55 | export function clampS8U8(v: number) { 56 | return fromS8U8(toS8U8(v)); 57 | } 58 | 59 | /** 60 | * Converts a number to signed 16 bits. 61 | */ 62 | export function toS16(v: number) { 63 | return (v << 16) >> 16; 64 | } 65 | 66 | export function bitCount(i: number): number { 67 | i = i - ((i >> 1) & 0x55555555); 68 | i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 69 | return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 70 | } 71 | 72 | export function ones(i: number): number { 73 | i = i - ((i >> 1) & 0x55555555); 74 | i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 75 | return ((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 76 | } 77 | 78 | export function trailingZeros(i: number): number { 79 | return ones((i & -i) - 1); 80 | } 81 | export function getFlags(i: number, flags: string[]): string { 82 | let str = ''; 83 | for (var i = 0; i < flags.length; i++) { 84 | if (i & (1 << i)) { 85 | str += flags[i] + ' '; 86 | } 87 | } 88 | if (str.length === 0) { 89 | return ''; 90 | } 91 | return str.trim(); 92 | } 93 | 94 | export function isPowerOfTwo(x: number) { 95 | return x && ((x & (x - 1)) === 0); 96 | } 97 | 98 | export function roundToMultipleOfFour(x: number) { 99 | return (x + 3) & ~0x3; 100 | } 101 | 102 | export function nearestPowerOfTwo(x: number) { 103 | x--; 104 | x |= x >> 1; 105 | x |= x >> 2; 106 | x |= x >> 4; 107 | x |= x >> 8; 108 | x |= x >> 16; 109 | x++; 110 | return x; 111 | } 112 | 113 | export function roundToMultipleOfPowerOfTwo(i: number, powerOfTwo: number) { 114 | const x = (1 << powerOfTwo) - 1; 115 | return (i + x) & ~x; // Round up to multiple of power of two. 116 | } 117 | 118 | export function toHEX(i: number) { 119 | var i = (i < 0 ? 0xFFFFFFFF + i + 1 : i); 120 | return '0x' + ('00000000' + i.toString(16)).substr(-8); 121 | } 122 | 123 | /** 124 | * Polyfill imul. 125 | */ 126 | if (!Math.imul) { 127 | Math.imul = function imul(a, b) { 128 | const ah = (a >>> 16) & 0xffff; 129 | const al = a & 0xffff; 130 | const bh = (b >>> 16) & 0xffff; 131 | const bl = b & 0xffff; 132 | // the shift by 0 fixes the sign on the high part 133 | // the final |0 converts the unsigned value into a signed value 134 | return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); 135 | }; 136 | } 137 | 138 | /** 139 | * Polyfill clz32. 140 | */ 141 | if (!Math.clz32) { 142 | Math.clz32 = function clz32(i: number) { 143 | i |= (i >> 1); 144 | i |= (i >> 2); 145 | i |= (i >> 4); 146 | i |= (i >> 8); 147 | i |= (i >> 16); 148 | return 32 - ones(i); 149 | }; 150 | } 151 | 152 | export const IntegerUtilities = { 153 | i8:i8, 154 | u8:u8, 155 | i32:i32, 156 | f32:f32, 157 | f64:f64, 158 | nativeLittleEndian:nativeLittleEndian, 159 | floatToInt32:floatToInt32, 160 | int32ToFloat:int32ToFloat, 161 | swap16:swap16, 162 | swap32:swap32, 163 | toS8U8:toS8U8, 164 | fromS8U8:fromS8U8, 165 | clampS8U8:clampS8U8, 166 | toS16:toS16, 167 | bitCount:bitCount, 168 | ones:ones, 169 | trailingZeros:trailingZeros, 170 | getFlags:getFlags, 171 | isPowerOfTwo:isPowerOfTwo, 172 | roundToMultipleOfFour:roundToMultipleOfFour, 173 | nearestPowerOfTwo:nearestPowerOfTwo, 174 | roundToMultipleOfPowerOfTwo:roundToMultipleOfPowerOfTwo, 175 | toHEX:toHEX 176 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/LocalConnectionService.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum LocalConnectionConnectResult { 3 | InvalidCallback = -3, 4 | AlreadyTaken = -2, 5 | InvalidName = -1, 6 | Success = 0 7 | } 8 | 9 | export const enum LocalConnectionCloseResult { 10 | NotConnected = -1, 11 | Success = 0 12 | } 13 | 14 | export interface ILocalConnectionReceiver { 15 | handleMessage(methodName: string, argsBuffer: ArrayBuffer): void; 16 | } 17 | 18 | export interface ILocalConnectionSender { 19 | dispatchEvent(event): void; 20 | hasEventListener(type: string): boolean; 21 | sec: any; 22 | } 23 | 24 | export interface ILocalConnectionService { 25 | createConnection(connectionName: string, 26 | receiver: ILocalConnectionReceiver): LocalConnectionConnectResult; 27 | closeConnection(connectionName: string, 28 | receiver: ILocalConnectionReceiver): LocalConnectionCloseResult; 29 | send(connectionName: string, methodName: string, args: ArrayBuffer, 30 | sender: ILocalConnectionSender, senderDomain: string, senderIsSecure: boolean): void; 31 | allowDomains(connectionName: string, receiver: ILocalConnectionReceiver, domains: string[], 32 | secure: boolean); 33 | } 34 | 35 | export var LocalConnectionService: ILocalConnectionService; 36 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/NumberUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | export function pow2(exponent: number): number { 3 | if (exponent === (exponent | 0)) { 4 | if (exponent < 0) { 5 | return 1 / (1 << -exponent); 6 | } 7 | return 1 << exponent; 8 | } 9 | return Math.pow(2, exponent); 10 | } 11 | 12 | export function clamp(value: number, min: number, max: number) { 13 | return Math.max(min, Math.min(max, value)); 14 | } 15 | 16 | /** 17 | * Rounds *.5 to the nearest even number. 18 | * See https://en.wikipedia.org/wiki/Rounding#Round_half_to_even for details. 19 | */ 20 | export function roundHalfEven(value: number): number { 21 | if (Math.abs(value % 1) === 0.5) { 22 | const floor = Math.floor(value); 23 | return floor % 2 === 0 ? floor : Math.ceil(value); 24 | } 25 | return Math.round(value); 26 | } 27 | 28 | /** 29 | * Rounds *.5 up on even occurrences, down on odd occurrences. 30 | * See https://en.wikipedia.org/wiki/Rounding#Alternating_tie-breaking for details. 31 | */ 32 | export function altTieBreakRound(value: number, even: boolean): number { 33 | if (Math.abs(value % 1) === 0.5 && !even) { 34 | return value | 0; 35 | } 36 | return Math.round(value); 37 | } 38 | 39 | export function epsilonEquals(value: number, other: number): boolean { 40 | return Math.abs(value - other) < 0.0000001; 41 | } 42 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/ObjectUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | import { assert, release } from './Debug'; 3 | import { isNullOrUndefined, isObject } from '../utilities'; 4 | 5 | export function boxValue(value) { 6 | if (isNullOrUndefined(value) || isObject(value)) { 7 | return value; 8 | } 9 | return Object(value); 10 | } 11 | 12 | export function toKeyValueArray(object: Object) { 13 | const hasOwnProperty = Object.prototype.hasOwnProperty; 14 | const array = []; 15 | for (const k in object) { 16 | if (hasOwnProperty.call(object, k)) { 17 | array.push([k, object[k]]); 18 | } 19 | } 20 | return array; 21 | } 22 | 23 | export function isPrototypeWriteable(object: Object) { 24 | return Object.getOwnPropertyDescriptor(object, 'prototype').writable; 25 | } 26 | 27 | export function hasOwnProperty(object: Object, name: string): boolean { 28 | return Object.prototype.hasOwnProperty.call(object, name); 29 | } 30 | 31 | export function propertyIsEnumerable(object: Object, name: string): boolean { 32 | return Object.prototype.propertyIsEnumerable.call(object, name); 33 | } 34 | 35 | /** 36 | * Returns a property descriptor for the own or inherited property with the given name, or 37 | * null if one doesn't exist. 38 | */ 39 | export function getPropertyDescriptor(object: Object, name: string): PropertyDescriptor { 40 | do { 41 | const propDesc = Object.getOwnPropertyDescriptor(object, name); 42 | if (propDesc) { 43 | return propDesc; 44 | } 45 | object = Object.getPrototypeOf(object); 46 | } while (object); 47 | return null; 48 | } 49 | 50 | export function hasOwnGetter(object: Object, name: string): boolean { 51 | const d = Object.getOwnPropertyDescriptor(object, name); 52 | return !!(d && d.get); 53 | } 54 | 55 | export function getOwnGetter(object: Object, name: string): () => any { 56 | const d = Object.getOwnPropertyDescriptor(object, name); 57 | return d ? d.get : null; 58 | } 59 | 60 | export function hasOwnSetter(object: Object, name: string): boolean { 61 | const d = Object.getOwnPropertyDescriptor(object, name); 62 | return !!(d && !!d.set); 63 | } 64 | 65 | export function defineReadOnlyProperty(object: Object, name: string, value: any) { 66 | Object.defineProperty(object, name, { 67 | value: value, 68 | writable: false, 69 | configurable: true, 70 | enumerable: false 71 | }); 72 | } 73 | 74 | export function copyOwnPropertyDescriptors(object: Object, 75 | template: Object, 76 | filter: (name: string) => boolean = null, 77 | overwrite = true, 78 | makeWritable = false) { 79 | 80 | const names = Object.getOwnPropertyNames(template); 81 | for (const property of names) { 82 | if (property != 'constructor' && hasOwnProperty(template, property) && (!filter || filter(property))) { 83 | const descriptor = Object.getOwnPropertyDescriptor(template, property); 84 | if (!overwrite && hasOwnProperty(object, property)) { 85 | continue; 86 | } 87 | release || assert (descriptor); 88 | try { 89 | if (makeWritable && descriptor.writable === false) { 90 | descriptor.writable = true; 91 | } 92 | Object.defineProperty(object, property, descriptor); 93 | } catch (e) { 94 | assert('Can\'t define: ' + property); 95 | } 96 | } 97 | } 98 | } 99 | 100 | export function copyPropertiesByList(object: Object, 101 | template: Object, 102 | propertyList: string []) { 103 | for (let i = 0; i < propertyList.length; i++) { 104 | const property = propertyList[i]; 105 | object[property] = template[property]; 106 | } 107 | } 108 | 109 | export function defineNonEnumerableGetter(obj, name, getter) { 110 | Object.defineProperty(obj, name, { get: getter, 111 | configurable: true, 112 | enumerable: false 113 | }); 114 | } 115 | 116 | export function defineNonEnumerableProperty(obj, name, value) { 117 | Object.defineProperty(obj, name, { value: value, 118 | writable: true, 119 | configurable: true, 120 | enumerable: false 121 | }); 122 | } -------------------------------------------------------------------------------- /lib/factories/base/utilities/PromiseWrapper.ts: -------------------------------------------------------------------------------- 1 | export class PromiseWrapper { 2 | public promise: Promise; 3 | public resolve: (result: T) => void; 4 | public reject: (reason) => void; 5 | 6 | then(onFulfilled, onRejected) { 7 | return this.promise.then(onFulfilled, onRejected); 8 | } 9 | 10 | constructor() { 11 | this.promise = new Promise( 12 | function(resolve, reject) { 13 | this.resolve = resolve; 14 | this.reject = reject; 15 | }.bind(this) 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/Shumway.ts: -------------------------------------------------------------------------------- 1 | /* 2 | export declare var Shumway: { 3 | version: string; 4 | build: string; 5 | }*/ 6 | export var Shumway = { 7 | version: '', 8 | build: '' 9 | }; 10 | 11 | export const enum CharacterCodes { 12 | _0 = 48, 13 | _1 = 49, 14 | _2 = 50, 15 | _3 = 51, 16 | _4 = 52, 17 | _5 = 53, 18 | _6 = 54, 19 | _7 = 55, 20 | _8 = 56, 21 | _9 = 57 22 | } -------------------------------------------------------------------------------- /lib/factories/base/utilities/StringUtilities.ts: -------------------------------------------------------------------------------- 1 | 2 | import { assert, release } from './Debug'; 3 | 4 | export function repeatString(c: string, n: number): string { 5 | let s = ''; 6 | for (let i = 0; i < n; i++) { 7 | s += c; 8 | } 9 | return s; 10 | } 11 | 12 | export function memorySizeToString(value: number) { 13 | value |= 0; 14 | const K = 1024; 15 | const M = K * K; 16 | if (value < K) { 17 | return value + ' B'; 18 | } else if (value < M) { 19 | return (value / K).toFixed(2) + 'KB'; 20 | } else { 21 | return (value / M).toFixed(2) + 'MB'; 22 | } 23 | } 24 | 25 | /** 26 | * Returns a reasonably sized description of the |value|, to be used for debugging purposes. 27 | */ 28 | export function toSafeString(value) { 29 | if (typeof value === 'string') { 30 | return '"' + value + '"'; 31 | } 32 | if (typeof value === 'number' || typeof value === 'boolean') { 33 | return String(value); 34 | } 35 | if (value instanceof Array) { 36 | return '[] ' + value.length; 37 | } 38 | return typeof value; 39 | } 40 | 41 | export function toSafeArrayString(array) { 42 | const str = []; 43 | for (let i = 0; i < array.length; i++) { 44 | str.push(toSafeString(array[i])); 45 | } 46 | return str.join(', '); 47 | } 48 | 49 | function utf8decode_impl(str: string): Uint8Array { 50 | const bytes = new Uint8Array(str.length * 4); 51 | let b = 0; 52 | for (let i = 0, j = str.length; i < j; i++) { 53 | let code = str.charCodeAt(i); 54 | if (code <= 0x7f) { 55 | bytes[b++] = code; 56 | continue; 57 | } 58 | 59 | if (0xD800 <= code && code <= 0xDBFF) { 60 | const codeLow = str.charCodeAt(i + 1); 61 | if (0xDC00 <= codeLow && codeLow <= 0xDFFF) { 62 | // convert only when both high and low surrogates are present 63 | code = ((code & 0x3FF) << 10) + (codeLow & 0x3FF) + 0x10000; 64 | ++i; 65 | } 66 | } 67 | 68 | if ((code & 0xFFE00000) !== 0) { 69 | bytes[b++] = 0xF8 | ((code >>> 24) & 0x03); 70 | bytes[b++] = 0x80 | ((code >>> 18) & 0x3F); 71 | bytes[b++] = 0x80 | ((code >>> 12) & 0x3F); 72 | bytes[b++] = 0x80 | ((code >>> 6) & 0x3F); 73 | bytes[b++] = 0x80 | (code & 0x3F); 74 | } else if ((code & 0xFFFF0000) !== 0) { 75 | bytes[b++] = 0xF0 | ((code >>> 18) & 0x07); 76 | bytes[b++] = 0x80 | ((code >>> 12) & 0x3F); 77 | bytes[b++] = 0x80 | ((code >>> 6) & 0x3F); 78 | bytes[b++] = 0x80 | (code & 0x3F); 79 | } else if ((code & 0xFFFFF800) !== 0) { 80 | bytes[b++] = 0xE0 | ((code >>> 12) & 0x0F); 81 | bytes[b++] = 0x80 | ((code >>> 6) & 0x3F); 82 | bytes[b++] = 0x80 | (code & 0x3F); 83 | } else { 84 | bytes[b++] = 0xC0 | ((code >>> 6) & 0x1F); 85 | bytes[b++] = 0x80 | (code & 0x3F); 86 | } 87 | } 88 | return bytes.slice(0, b); 89 | } 90 | 91 | function utf8encode_impl(bytes: Uint8Array): string { 92 | let j = 0, str = ''; 93 | while (j < bytes.length) { 94 | const b1 = bytes[j++] & 0xFF; 95 | if (b1 <= 0x7F) { 96 | str += String.fromCharCode(b1); 97 | } else { 98 | let currentPrefix = 0xC0; 99 | let validBits = 5; 100 | do { 101 | const mask = (currentPrefix >> 1) | 0x80; 102 | if ((b1 & mask) === currentPrefix) break; 103 | currentPrefix = (currentPrefix >> 1) | 0x80; 104 | --validBits; 105 | } while (validBits >= 0); 106 | 107 | if (validBits <= 0) { 108 | // Invalid UTF8 character -- copying as is 109 | str += String.fromCharCode(b1); 110 | continue; 111 | } 112 | let code = (b1 & ((1 << validBits) - 1)); 113 | let invalid = false; 114 | for (var i = 5; i >= validBits; --i) { 115 | const bi = bytes[j++]; 116 | if ((bi & 0xC0) != 0x80) { 117 | // Invalid UTF8 character sequence 118 | invalid = true; 119 | break; 120 | } 121 | code = (code << 6) | (bi & 0x3F); 122 | } 123 | if (invalid) { 124 | // Copying invalid sequence as is 125 | for (let k = j - (7 - i); k < j; ++k) { 126 | str += String.fromCharCode(bytes[k] & 255); 127 | } 128 | continue; 129 | } 130 | if (code >= 0x10000) { 131 | str += String.fromCharCode((((code - 0x10000) >> 10) & 0x3FF) | 132 | 0xD800, (code & 0x3FF) | 0xDC00); 133 | } else { 134 | str += String.fromCharCode(code); 135 | } 136 | } 137 | } 138 | return str; 139 | } 140 | 141 | const textEncoder = self.TextEncoder ? new self.TextEncoder() : null; 142 | const textDecoder = self.TextDecoder ? new self.TextDecoder() : null; 143 | 144 | let useNativeUT8: boolean = true; 145 | 146 | export function setNativeUTF8Decoding(value: boolean) { 147 | useNativeUT8 = value; 148 | } 149 | 150 | export function getNativeUTF8Decoding() { 151 | return useNativeUT8; 152 | } 153 | 154 | /** 155 | * Encoder utf to byte array 156 | * @param str string 157 | */ 158 | export function utf8decode(str: string): Uint8Array { 159 | if (!textEncoder || !useNativeUT8) 160 | return utf8decode_impl(str); 161 | 162 | try { 163 | return textEncoder.encode(str); 164 | } catch (e) { 165 | return utf8decode_impl(str); 166 | } 167 | } 168 | 169 | /** 170 | * Decode utf string from byte array 171 | * @param buffer 172 | */ 173 | export function utf8encode(buffer: Uint8Array): string { 174 | if (!textDecoder || !useNativeUT8) 175 | return utf8encode_impl(buffer); 176 | 177 | try { 178 | return textDecoder.decode(buffer); 179 | } catch (e) { 180 | return utf8encode_impl(buffer); 181 | } 182 | } 183 | 184 | export function base64EncodeBytes(bytes: Uint8Array) { 185 | return self.btoa(fromCharCodeArray(bytes)); 186 | } 187 | 188 | const base64DecodeMap = [ // starts at 0x2B 189 | 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 190 | 0, 0, 0, 0, 0, 0, 0, // 0x3A-0x40 191 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 192 | 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, // 0x5B-0x0x60 193 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 194 | 44, 45, 46, 47, 48, 49, 50, 51]; 195 | const base64DecodeMapOffset = 0x2B; 196 | const base64EOF = 0x3D; 197 | 198 | /** 199 | * Decodes the result of encoding with base64EncodeBytes, but not necessarily any other 200 | * base64-encoded data. Note that this also doesn't do any error checking. 201 | */ 202 | export function decodeRestrictedBase64ToBytes(encoded: string): Uint8Array { 203 | const byteStr = window.atob(encoded); 204 | const len = byteStr.length; 205 | const bytes = new Uint8Array(len); 206 | 207 | for (let i = 0; i < len; i++) { 208 | bytes[i] = byteStr.charCodeAt(i); 209 | } 210 | 211 | return bytes; 212 | } 213 | 214 | export function escapeString(str: string) { 215 | if (str !== undefined) { 216 | str = str.replace(/[^\w$]/gi,'$'); /* No dots, colons, dashes and /s */ 217 | if (/^\d/.test(str)) { /* No digits at the beginning */ 218 | str = '$' + str; 219 | } 220 | } 221 | return str; 222 | } 223 | 224 | /** 225 | * Workaround for max stack size limit. 226 | */ 227 | export function fromCharCodeArray(buffer: Uint8Array | Uint32Array): string { 228 | let str = '', SLICE = 1024 * 5; 229 | for (let i = 0; i < buffer.length; i += SLICE) { 230 | const chunk = Math.min(buffer.length - i, SLICE); 231 | str += String.fromCharCode.apply(null, buffer.subarray(i, i + chunk)); 232 | } 233 | return str; 234 | } 235 | 236 | const _encoding = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_'; 237 | export function variableLengthEncodeInt32(n) { 238 | const e = _encoding; 239 | const bitCount = (32 - Math.clz32(n)); 240 | release || assert (bitCount <= 32, bitCount); 241 | const l = Math.ceil(bitCount / 6); 242 | // Encode length followed by six bit chunks. 243 | let s = e[l]; 244 | for (let i = l - 1; i >= 0; i--) { 245 | const offset = (i * 6); 246 | s += e[(n >> offset) & 0x3F]; 247 | } 248 | release || assert (StringUtilities.variableLengthDecodeInt32(s) === n, n + ' : ' + s + ' - ' + l + ' bits: ' + bitCount); 249 | return s; 250 | } 251 | 252 | export function toEncoding(n) { 253 | return _encoding[n]; 254 | } 255 | 256 | export function fromEncoding(c) { 257 | if (c >= 65 && c <= 90) { 258 | return c - 65; 259 | } else if (c >= 97 && c <= 122) { 260 | return c - 71; 261 | } else if (c >= 48 && c <= 57) { 262 | return c + 4; 263 | } else if (c === 36) { 264 | return 62; 265 | } else if (c === 95) { 266 | return 63; 267 | } 268 | release || assert (false, 'Invalid Encoding'); 269 | } 270 | 271 | export function variableLengthDecodeInt32(s) { 272 | const l = fromEncoding(s.charCodeAt(0)); 273 | let n = 0; 274 | for (let i = 0; i < l; i++) { 275 | const offset = ((l - i - 1) * 6); 276 | n |= fromEncoding(s.charCodeAt(1 + i)) << offset; 277 | } 278 | return n; 279 | } 280 | 281 | export function trimMiddle(s: string, maxLength: number): string { 282 | if (s.length <= maxLength) { 283 | return s; 284 | } 285 | const leftHalf = maxLength >> 1; 286 | const rightHalf = maxLength - leftHalf - 1; 287 | return s.substr(0, leftHalf) + '\u2026' + s.substr(s.length - rightHalf, rightHalf); 288 | } 289 | 290 | export function multiple(s: string, count: number): string { 291 | let o = ''; 292 | for (let i = 0; i < count; i++) { 293 | o += s; 294 | } 295 | return o; 296 | } 297 | 298 | export function indexOfAny(s: string, chars: string [], position: number) { 299 | let index = s.length; 300 | for (let i = 0; i < chars.length; i++) { 301 | const j = s.indexOf(chars[i], position); 302 | if (j >= 0) { 303 | index = Math.min(index, j); 304 | } 305 | } 306 | return index === s.length ? -1 : index; 307 | } 308 | 309 | const _concat3array = new Array(3); 310 | const _concat4array = new Array(4); 311 | const _concat9array = new Array(9); 312 | 313 | /** 314 | * The concatN() functions concatenate multiple strings in a way that 315 | * avoids creating intermediate strings, unlike String.prototype.concat(). 316 | * 317 | * Note that these functions don't have identical behaviour to using '+', 318 | * because they will ignore any arguments that are |undefined| or |null|. 319 | * This usually doesn't matter. 320 | */ 321 | 322 | export function concat3(s0: any, s1: any, s2: any) { 323 | _concat3array[0] = s0; 324 | _concat3array[1] = s1; 325 | _concat3array[2] = s2; 326 | return _concat3array.join(''); 327 | } 328 | 329 | export function concat4(s0: any, s1: any, s2: any, s3: any) { 330 | _concat4array[0] = s0; 331 | _concat4array[1] = s1; 332 | _concat4array[2] = s2; 333 | _concat4array[3] = s3; 334 | return _concat4array.join(''); 335 | } 336 | 337 | export function concat9(s0: any, s1: any, s2: any, s3: any, s4: any, 338 | s5: any, s6: any, s7: any, s8: any) { 339 | _concat9array[0] = s0; 340 | _concat9array[1] = s1; 341 | _concat9array[2] = s2; 342 | _concat9array[3] = s3; 343 | _concat9array[4] = s4; 344 | _concat9array[5] = s5; 345 | _concat9array[6] = s6; 346 | _concat9array[7] = s7; 347 | _concat9array[8] = s8; 348 | return _concat9array.join(''); 349 | } 350 | 351 | export const StringUtilities = { 352 | repeatString:repeatString, 353 | memorySizeToString:memorySizeToString, 354 | toSafeString:toSafeString, 355 | toSafeArrayString:toSafeArrayString, 356 | utf8decode:utf8decode, 357 | utf8encode:utf8encode, 358 | base64EncodeBytes:base64EncodeBytes, 359 | base64DecodeMap:base64DecodeMap, 360 | base64DecodeMapOffset:base64DecodeMapOffset, 361 | base64EOF:base64EOF, 362 | decodeRestrictedBase64ToBytes:decodeRestrictedBase64ToBytes, 363 | escapeString:escapeString, 364 | fromCharCodeArray:fromCharCodeArray, 365 | variableLengthEncodeInt32:variableLengthEncodeInt32, 366 | toEncoding:toEncoding, 367 | fromEncoding:fromEncoding, 368 | variableLengthDecodeInt32:variableLengthDecodeInt32, 369 | trimMiddle:trimMiddle, 370 | multiple:multiple, 371 | indexOfAny:indexOfAny, 372 | _concat3array:_concat3array, 373 | _concat4array:_concat4array, 374 | _concat9array:_concat9array, 375 | concat3:concat3, 376 | concat4:concat4, 377 | concat9:concat9 378 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/SystemResourcesLoadingService.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum SystemResourceId { 3 | BuiltinAbc = 0, 4 | PlayerglobalAbcs = 1, 5 | PlayerglobalManifest = 2, 6 | ShellAbc = 3 7 | } 8 | 9 | export interface ISystemResourcesLoadingService { 10 | load(id: SystemResourceId): Promise; 11 | } 12 | 13 | export var instance: ISystemResourcesLoadingService; 14 | 15 | export const SystemResourcesLoadingService = { 16 | //SystemResourceId:SystemResourceId, 17 | //ISystemResourcesLoadingService:ISystemResourcesLoadingService 18 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/Telemetry.ts: -------------------------------------------------------------------------------- 1 | 2 | export const enum Feature { 3 | EXTERNAL_INTERFACE_FEATURE = 1, 4 | CLIPBOARD_FEATURE = 2, 5 | SHAREDOBJECT_FEATURE = 3, 6 | VIDEO_FEATURE = 4, 7 | SOUND_FEATURE = 5, 8 | NETCONNECTION_FEATURE = 6 9 | } 10 | 11 | export const enum ErrorTypes { 12 | AVM1_ERROR = 1, 13 | AVM2_ERROR = 2 14 | } 15 | 16 | export const enum LoadResource { 17 | LoadSource = 0, 18 | LoadWhitelistAllowed = 1, 19 | LoadWhitelistDenied = 2, 20 | StreamAllowed = 3, 21 | StreamDenied = 4, 22 | StreamCrossdomain = 5 23 | } 24 | 25 | export interface ITelemetryService { 26 | reportTelemetry(data: any); 27 | } 28 | 29 | export var instance: ITelemetryService; 30 | 31 | export const Telemetry = { 32 | instance:instance 33 | }; 34 | -------------------------------------------------------------------------------- /lib/factories/base/utilities/UI.ts: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Converts a |MouseCursor| number to a CSS |cursor| property value. 4 | */ 5 | export function toCSSCursor(mouseCursor: number) { 6 | switch (mouseCursor) { 7 | case 0: // MouseCursor.AUTO 8 | return 'auto'; 9 | case 2: // MouseCursor.BUTTON 10 | return 'pointer'; 11 | case 3: // MouseCursor.HAND 12 | return 'grab'; 13 | case 4: // MouseCursor.IBEAM 14 | return 'text'; 15 | case 1: // MouseCursor.ARROW 16 | default: 17 | return 'default'; 18 | } 19 | 20 | } 21 | export const UI = { 22 | toCSSCursor:toCSSCursor 23 | }; -------------------------------------------------------------------------------- /lib/factories/base/utilities/jsGlobal.ts: -------------------------------------------------------------------------------- 1 | export var jsGlobal = Function('return this')(); -------------------------------------------------------------------------------- /lib/factories/timelinesounds/MovieClipSoundStream.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SoundStream, packageWave } from '../../parsers/utils/parser/sound'; 3 | import { WaveAudio } from '@awayjs/core'; 4 | import { WaveAudioData } from '@awayjs/core'; 5 | import { MovieClipSoundsManager } from './MovieClipSoundsManager'; 6 | import { release } from '../base/utilities/Debug'; 7 | 8 | export interface ISampeFrameInfo { 9 | channels: number; 10 | sampleRate: number; 11 | streamSize: number; 12 | } 13 | 14 | export interface DecodedSound { 15 | streamId: number; 16 | samplesCount: number; 17 | pcm?: Float32Array; 18 | data?: Uint8Array; 19 | seek?: number; 20 | } 21 | 22 | interface ISoundStreamAdapter { 23 | currentTime: number; 24 | paused: boolean; 25 | isReady: boolean; 26 | isComplete: boolean; 27 | byteLength: number; 28 | 29 | stop(): void; 30 | playFrom(time: number): void; 31 | queueData(frame: DecodedSound): void; 32 | finish(): void; 33 | isPlaying: boolean; 34 | } 35 | 36 | class WebAudioAdapter implements ISoundStreamAdapter { 37 | protected _sound: WaveAudio; 38 | protected _data: ISampeFrameInfo; 39 | protected _frameData: DecodedSound[]; 40 | protected _position: number; 41 | protected _byteLength: number = 0; 42 | 43 | get byteLength() { 44 | return this._byteLength; 45 | } 46 | 47 | get isComplete() { 48 | return this._byteLength > 0; 49 | } 50 | 51 | get currentTime(): number { 52 | return this._sound.currentTime; 53 | } 54 | 55 | get sound(): WaveAudio { 56 | return this._sound; 57 | } 58 | 59 | playFrom(time: number) { 60 | /*var startPlay=true; 61 | if(this._sound.duration; 159 | private position: number; 160 | private finalized: boolean; 161 | public isPlaying: boolean; 162 | private _stopped: boolean; 163 | private isMP3: boolean; 164 | private soundStreamAdapter: WebAudioAdapter; 165 | 166 | private decode: (block: Uint8Array) => DecodedSound; 167 | 168 | public constructor(streamInfo: SoundStream) { 169 | this._stopped = false; 170 | this.decode = streamInfo.decode.bind(streamInfo); 171 | this.data = { 172 | sampleRate: streamInfo.sampleRate, 173 | channels: streamInfo.channels, 174 | streamSize: streamInfo.streamSize, 175 | }; 176 | this.seekIndex = []; 177 | this.position = 0; 178 | 179 | this.isMP3 = streamInfo.format === 'mp3'; 180 | this.soundStreamAdapter = !this.isMP3 181 | ? new WebAudioAdapter(this.data) 182 | : new WebAudioMP3Adapter(this.data); 183 | 184 | } 185 | 186 | public appendBlock(frameNum: number, streamBlock: Uint8Array) { 187 | const decodedBlock = this.decode(streamBlock); 188 | const streamPosition = this.position; 189 | 190 | this.seekIndex[frameNum] = (streamPosition + decodedBlock.seek); 191 | this.position = streamPosition + decodedBlock.samplesCount; 192 | this.soundStreamAdapter.queueData(decodedBlock); 193 | } 194 | 195 | public get stopped(): boolean { 196 | return this._stopped; 197 | } 198 | 199 | public set stopped(value: boolean) { 200 | this._stopped = value; 201 | } 202 | 203 | public stop() { 204 | this.soundStreamAdapter.stop(); 205 | } 206 | 207 | public playFrame(frameNum: number): number { 208 | if (this._stopped || isNaN(this.seekIndex[frameNum])) { 209 | return 0; 210 | } 211 | 212 | if (!this.soundStreamAdapter.isComplete) { 213 | return 0; 214 | } 215 | 216 | if (!this.finalized) { 217 | this.finalized = true; 218 | this.soundStreamAdapter.finish(); 219 | } 220 | 221 | this.isPlaying = true; 222 | 223 | const soundStreamData = this.data; 224 | 225 | let time = this.seekIndex[frameNum] / soundStreamData.channels / soundStreamData.sampleRate; 226 | 227 | if (this.isMP3) { 228 | time *= soundStreamData.channels; 229 | } 230 | 231 | MovieClipSoundsManager.addActiveSound(this.soundStreamAdapter.sound); 232 | 233 | if (!this.soundStreamAdapter.isPlaying) { 234 | this.soundStreamAdapter.playFrom(time); 235 | } 236 | 237 | const elementTime = this.soundStreamAdapter.currentTime; 238 | 239 | let framestoSkip = (elementTime - time) * MovieClipSoundsManager.frameRate; 240 | 241 | if ((elementTime - time) < 0) { 242 | framestoSkip = Math.ceil(framestoSkip); 243 | } else { 244 | framestoSkip = Math.floor(framestoSkip); 245 | } 246 | 247 | //console.log("framestoSkip ", framestoSkip); 248 | return framestoSkip; 249 | 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /lib/factories/timelinesounds/MovieClipSoundsManager.ts: -------------------------------------------------------------------------------- 1 | 2 | import { MovieClipSoundStream } from './MovieClipSoundStream'; 3 | import { WaveAudio } from '@awayjs/core'; 4 | import { release } from '../base/utilities/Debug'; 5 | 6 | export class MovieClipSoundsManager { 7 | private lastFrameNum: number; 8 | private soundStreamHead: any; 9 | private _soundStreams: MovieClipSoundStream[]; 10 | private _frameToSoundStream: NumberMap; 11 | 12 | public static frameRate: number = 24; 13 | 14 | private static activeSounds = {}; 15 | 16 | public static addActiveSound(sound: WaveAudio) { 17 | //console.log("start sound with id", sound.id) 18 | if (!MovieClipSoundsManager.activeSounds[sound.id]) { 19 | MovieClipSoundsManager.activeSounds[sound.id] = {}; 20 | } 21 | MovieClipSoundsManager.activeSounds[sound.id].active = true; 22 | MovieClipSoundsManager.activeSounds[sound.id].sound = sound; 23 | } 24 | 25 | public static enterFrame() { 26 | for (const key in MovieClipSoundsManager.activeSounds) { 27 | //console.log("set sound to inactive", key) 28 | const sound = MovieClipSoundsManager.activeSounds[key]; 29 | sound.active = false; 30 | } 31 | } 32 | 33 | public static exitFrame() { 34 | for (const key in MovieClipSoundsManager.activeSounds) { 35 | const sound = MovieClipSoundsManager.activeSounds[key]; 36 | if (!sound.active) { 37 | //console.log("stop inactive sound", key) 38 | sound.sound.stop(); 39 | delete MovieClipSoundsManager.activeSounds[key]; 40 | } 41 | } 42 | } 43 | 44 | constructor() { 45 | this.lastFrameNum = -2; 46 | this._soundStreams = []; 47 | this._frameToSoundStream = {}; 48 | 49 | } 50 | 51 | public initSoundStream(streamInfo: any) { 52 | this.soundStreamHead = streamInfo; 53 | } 54 | 55 | public addSoundStreamBlock(frameNum: number, streamBlock: Uint8Array) { 56 | if (!this.soundStreamHead) { 57 | release || console.log('can not add soundstreamblock if no soundstreamHead exists'); 58 | return; 59 | } 60 | if (this._soundStreams.length == 0 || (frameNum - this.lastFrameNum) > 1) { 61 | this._soundStreams.push(new MovieClipSoundStream(this.soundStreamHead)); 62 | } 63 | this.lastFrameNum = frameNum; 64 | this._soundStreams[this._soundStreams.length - 1].appendBlock(frameNum, streamBlock); 65 | this._frameToSoundStream[frameNum] = this._soundStreams[this._soundStreams.length - 1]; 66 | 67 | } 68 | 69 | public stopStream(frameNum: number) { 70 | if (!this._frameToSoundStream[frameNum]) 71 | return; 72 | this._frameToSoundStream[frameNum].stopped = true; 73 | } 74 | 75 | public resetStreamStopped() { 76 | for (let i = 0; i < this._soundStreams.length; i++) { 77 | this._soundStreams[i].stopped = false; 78 | } 79 | } 80 | 81 | syncSounds(frameNum: number, isPlaying: boolean, parent: any): number { 82 | if (isPlaying && parent && this._frameToSoundStream[frameNum] && !this._frameToSoundStream[frameNum].stopped) { 83 | return this._frameToSoundStream[frameNum].playFrame(frameNum); 84 | } 85 | return 0; 86 | } 87 | } -------------------------------------------------------------------------------- /lib/factories/timelinesounds/mp3decodersession.ts: -------------------------------------------------------------------------------- 1 | import { PromiseWrapper } from '../base/utilities'; 2 | 3 | export var MP3WORKER_PATH: string = './assets/mp3worker.js'; 4 | 5 | let mp3Worker: Worker = null; 6 | 7 | function ensureMP3Worker(): Worker { 8 | if (!mp3Worker) { 9 | mp3Worker = new Worker(MP3WORKER_PATH); 10 | mp3Worker.addEventListener('message', function (e) { 11 | if (e.data.action === 'console') { 12 | console[e.data.method].call(console, e.data.message); 13 | } 14 | }); 15 | } 16 | return mp3Worker; 17 | } 18 | 19 | let nextSessionId: number = 0; 20 | 21 | export class MP3DecoderSession { 22 | private _sessionId: number; 23 | private _onworkermessageBound: (any) => void; 24 | private _worker: Worker; 25 | 26 | public onframedata: (frameData: Uint8Array, channels: number, sampleRate: number, bitRate: number) => void; 27 | public onid3tag: (tagData: any) => void; 28 | public onclosed: () => void; 29 | public onerror: (error: string) => void; 30 | 31 | public constructor() { 32 | this._sessionId = (nextSessionId++); 33 | this._onworkermessageBound = this.onworkermessage.bind(this); 34 | this._worker = ensureMP3Worker(); 35 | this._worker.addEventListener('message', this._onworkermessageBound, false); 36 | this._worker.postMessage({ 37 | sessionId: this._sessionId, 38 | action: 'create' 39 | }); 40 | } 41 | 42 | private onworkermessage(e) { 43 | if (e.data.sessionId !== this._sessionId) 44 | return; 45 | const action = e.data.action; 46 | switch (action) { 47 | case 'closed': 48 | if (this.onclosed) { 49 | this.onclosed(); 50 | } 51 | this._worker.removeEventListener('message', this._onworkermessageBound, false); 52 | this._worker = null; 53 | break; 54 | case 'frame': 55 | this.onframedata(e.data.frameData, e.data.channels, 56 | e.data.sampleRate, e.data.bitRate); 57 | break; 58 | case 'id3': 59 | if (this.onid3tag) { 60 | this.onid3tag(e.data.id3Data); 61 | } 62 | break; 63 | case 'error': 64 | if (this.onerror) { 65 | this.onerror(e.data.message); 66 | } 67 | break; 68 | } 69 | } 70 | 71 | pushAsync(data) { 72 | this._worker.postMessage({ 73 | sessionId: this._sessionId, 74 | action: 'decode', 75 | data: data 76 | }); 77 | } 78 | 79 | close() { 80 | this._worker.postMessage({ 81 | sessionId: this._sessionId, 82 | action: 'close' 83 | }); 84 | } 85 | 86 | public static processAll(data: Uint8Array): Promise<{ data: Uint8Array; id3Tags: any; }> { 87 | let currentBufferSize = 8000; 88 | let currentBuffer = new Float32Array(currentBufferSize); 89 | let bufferPosition = 0; 90 | const id3Tags = []; 91 | let sessionAborted = false; 92 | 93 | const promiseWrapper = new PromiseWrapper<{ data: Uint8Array; id3Tags: any; }>(); 94 | const session = new MP3DecoderSession(); 95 | session.onframedata = function (frameData, channels, sampleRate, bitRate) { 96 | const needed = frameData.length + bufferPosition; 97 | if (needed > currentBufferSize) { 98 | do { 99 | currentBufferSize *= 2; 100 | } while (needed > currentBufferSize); 101 | const newBuffer = new Float32Array(currentBufferSize); 102 | newBuffer.set(currentBuffer); 103 | currentBuffer = newBuffer; 104 | } 105 | currentBuffer.set(frameData, bufferPosition); 106 | bufferPosition += frameData.length; 107 | }; 108 | session.onid3tag = function (tagData) { 109 | id3Tags.push(tagData); 110 | }; 111 | session.onclosed = function () { 112 | if (sessionAborted) 113 | return; 114 | promiseWrapper.resolve({ data: currentBuffer.subarray(0, bufferPosition), id3Tags: id3Tags }); 115 | }; 116 | session.onerror = function (error) { 117 | if (sessionAborted) 118 | return; 119 | sessionAborted = true; 120 | promiseWrapper.reject(error); 121 | }; 122 | session.pushAsync(data); 123 | session.close(); 124 | 125 | return promiseWrapper.promise; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/parsers/CompressionMethod.ts: -------------------------------------------------------------------------------- 1 | 2 | export enum CompressionMethod { 3 | None, 4 | Deflate, 5 | LZMA 6 | } -------------------------------------------------------------------------------- /lib/parsers/EagerlyParsedDictionaryEntry.ts: -------------------------------------------------------------------------------- 1 | import { UnparsedTag, DictionaryEntry } from '@awayjs/graphics'; 2 | 3 | export declare class EagerlyParsedDictionaryEntry extends DictionaryEntry { 4 | type: string; 5 | definition: Object; 6 | env: any; 7 | ready: boolean; 8 | constructor(id: number, unparsed: UnparsedTag, type: string, definition: any, env: any); 9 | } -------------------------------------------------------------------------------- /lib/parsers/FlashWaveAudioParser.ts: -------------------------------------------------------------------------------- 1 | import { ParserBase, WaveAudio, WaveAudioData, WaveAudioParser } from '@awayjs/core'; 2 | import { ISoundSymbol } from './../parsers/ISymbol'; 3 | 4 | export class FlashWaveAudioParser extends WaveAudioParser { 5 | protected proceedParsing(): void { 6 | const data: ISoundSymbol = this.data; 7 | const define = data.definition!; 8 | 9 | const meta = { 10 | sampleRate: define.sampleRate, 11 | samplesCount: define.samplesCount, 12 | startOffset: define.packaged.seek, 13 | }; 14 | //@ts-ignore 15 | this._content = new WaveAudio(new WaveAudioData(define.packaged.data.buffer, meta)); 16 | this.finalizeAsset(this._content, this.fileName); 17 | 18 | this.finishParsing(); 19 | } 20 | } -------------------------------------------------------------------------------- /lib/parsers/ISymbol.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Bbox, Shape, ShapeTag } from '@awayjs/graphics'; 3 | import { SWFFrame } from './SWFFrame'; 4 | import { UnparsedTag } from '@awayjs/graphics'; 5 | import { TextTag, BinaryDataTag, VideoStreamTag, FontTag } from '../factories/base/SWFTags'; 6 | 7 | export const enum SYMBOL_TYPE { 8 | SHAPE = 'shape', 9 | MORPH = 'morphshape', 10 | FONT = 'font', 11 | SPRITE = 'sprite', 12 | TEXT = 'text', 13 | SOUND = 'sound', 14 | BUTTON = 'button', 15 | LABEL = 'label', 16 | IMAGE = 'image', 17 | BINARY = 'binary', 18 | VIDEO = 'video' 19 | } 20 | 21 | export interface ISymbol extends UnparsedTag { 22 | className: string; 23 | name: string; 24 | id: number; 25 | type: SYMBOL_TYPE; 26 | away: any; 27 | tag: any; 28 | scalingGrid?: any; 29 | } 30 | 31 | export interface IShapeSymbol extends ISymbol, ShapeTag { 32 | shape?: Shape & {className: string, }; 33 | } 34 | 35 | export interface ISpriteSymbol extends ISymbol { 36 | frames: SWFFrame[]; 37 | } 38 | 39 | export interface ITextSymbol extends ISymbol { 40 | tag: TextTag & {letterSpacing: number}; 41 | fillBounds: Bbox; 42 | } 43 | 44 | export interface IButtonSymbol extends ISymbol { 45 | states: any; 46 | buttonActions: any; 47 | buttonSounds: any; 48 | } 49 | 50 | export interface ILabelSymbol extends ITextSymbol { 51 | records: any[]; 52 | matrix: number[]; 53 | } 54 | 55 | export interface IImageSymbol extends ISymbol { 56 | definition: { 57 | width: number; 58 | height: number; 59 | data: Uint8ClampedArray; 60 | isPMA?: boolean; 61 | }, 62 | needParse: boolean, 63 | lazyParser: () => IImageSymbol; 64 | } 65 | 66 | export interface ISoundSymbol extends ISymbol { 67 | definition: { 68 | packaged?: { 69 | data: Uint8Array; 70 | mimeType: string; 71 | seek: number; 72 | }; 73 | pcm: any; 74 | sampleRate: number; 75 | samplesCount: number; 76 | channels: number; 77 | } 78 | } 79 | 80 | export interface IBinarySymbol extends BinaryDataTag, ISymbol {} 81 | export interface IVideoSymbol extends VideoStreamTag, ISymbol {} 82 | export interface IFontSymbol extends FontTag, ISymbol {} 83 | -------------------------------------------------------------------------------- /lib/parsers/SWFFile.ts: -------------------------------------------------------------------------------- 1 | import { CompressionMethod } from './CompressionMethod'; 2 | import { SWFFrame } from './SWFFrame'; 3 | import { EagerlyParsedDictionaryEntry } from './EagerlyParsedDictionaryEntry'; 4 | import { Bounds, DictionaryEntry, ABCBlock } from '@awayjs/graphics'; 5 | 6 | export class SWFFile { 7 | public hash: string = ''; 8 | public compression: number;//CompressionMethod; 9 | public swfVersion: number; 10 | public fpVersion: string; 11 | public url: string; 12 | public useAVM1: boolean; 13 | public backgroundColor: number; 14 | public bounds: Bounds;//80pro: todo 15 | public frameRate: number; 16 | public frameCount: number; 17 | public attributes: any; // TODO: type strongly 18 | public sceneAndFrameLabelData: any; // TODO: type strongly 19 | 20 | public bytesLoaded: number; 21 | public bytesTotal: number; 22 | public pendingUpdateDelays: number; 23 | // Might be lower than frames.length if eagerly parsed assets pending resolution are blocking 24 | // us from reporting the given frame as loaded. 25 | public framesLoaded: number; 26 | 27 | public frames: SWFFrame[]; 28 | public abcBlocks: ABCBlock[]; 29 | public dictionary: DictionaryEntry[]; 30 | public fonts: {name: string; style: string; id: number}[]; 31 | public data: Uint8Array; 32 | public env: any; 33 | 34 | public symbolClassesMap: string[]; 35 | public symbolClassesList: {id: number; className: string}[]; 36 | public eagerlyParsedSymbolsMap: EagerlyParsedDictionaryEntry[]; 37 | public eagerlyParsedSymbolsList: EagerlyParsedDictionaryEntry[]; 38 | 39 | constructor() { 40 | 41 | this.compression = CompressionMethod.None; 42 | this.swfVersion = 0; 43 | this.useAVM1 = true; 44 | this.backgroundColor = 0xffffffff; 45 | this.bounds = null; 46 | this.frameRate = 0; 47 | this.frameCount = 0; 48 | this.attributes = null; 49 | this.sceneAndFrameLabelData = null; 50 | 51 | this.bytesLoaded = 0; 52 | this.bytesTotal = length; 53 | this.pendingUpdateDelays = 0; 54 | this.framesLoaded = 0; 55 | } 56 | 57 | public mapSWFVersionToFPVersion() { 58 | const swfToFPVersion: NumberMap = { 59 | 6:'FP6', 60 | 7:'FP7', 61 | 8:'FP8', 62 | 9:'FP9', 63 | 10:'FP10', 64 | 11:'FP10_2', 65 | 12:'FP10_3', 66 | 13:'FP11_0', 67 | 14:'FP11_1', 68 | 15:'FP11_2', 69 | 16:'FP11_3', 70 | 17:'FP11_4', 71 | 18:'FP11_5', 72 | 19:'FP11_6', 73 | 20:'FP11_7', 74 | 21:'FP11_8', 75 | 22:'FP11_9', 76 | 23:'FP12', 77 | 24:'FP13', 78 | 25:'FP14', 79 | 26:'FP15', 80 | 27:'FP16', 81 | 28:'FP17', 82 | 29:'FP18', 83 | 30:'FP19', 84 | 31:'FP20', 85 | 32:'FP21', 86 | 33:'FP22', 87 | 34:'FP23', 88 | 35:'FP24', 89 | 36:'FP25', 90 | 37:'FP26', 91 | 38:'FP27', 92 | 39:'FP28', 93 | 40:'FP29', 94 | 41:'FP30', 95 | 42:'FP31' 96 | }; 97 | this.fpVersion = swfToFPVersion[this.swfVersion] ? swfToFPVersion[this.swfVersion] : 'unmappedSWFVersion:' + this.swfVersion; 98 | } 99 | } -------------------------------------------------------------------------------- /lib/parsers/SWFFrame.ts: -------------------------------------------------------------------------------- 1 | 2 | import { SoundStream } from '../parsers/utils/parser/sound'; 3 | 4 | import { 5 | ActionBlock, 6 | InitActionBlock, 7 | SymbolExport, 8 | UnparsedTag } from '@awayjs/graphics'; 9 | export class SWFFrame { 10 | controlTags: UnparsedTag[]; 11 | labelNames: string[]; 12 | soundStreamHead: SoundStream; 13 | soundStreamBlock: Uint8Array; 14 | actionBlocks: ActionBlock[]; 15 | initActionBlocks: InitActionBlock[]; 16 | exports: SymbolExport[]; 17 | buttonStateName: string; 18 | 19 | constructor(controlTags?: UnparsedTag[], labelNames?: string[], 20 | soundStreamHead?: SoundStream, 21 | soundStreamBlock?: Uint8Array, 22 | actionBlocks?: ActionBlock[], 23 | initActionBlocks?: InitActionBlock[], 24 | exports?: SymbolExport[]) { 25 | controlTags && Object.freeze(controlTags); 26 | this.controlTags = controlTags; 27 | this.labelNames = labelNames; 28 | actionBlocks && Object.freeze(actionBlocks); 29 | this.soundStreamHead = soundStreamHead; 30 | this.soundStreamBlock = soundStreamBlock; 31 | this.actionBlocks = actionBlocks; 32 | initActionBlocks && Object.freeze(initActionBlocks); 33 | this.initActionBlocks = initActionBlocks; 34 | this.buttonStateName = ''; 35 | exports && Object.freeze(exports); 36 | this.exports = exports; 37 | } 38 | } -------------------------------------------------------------------------------- /lib/parsers/utils/parser/OpenTypeParser.ts: -------------------------------------------------------------------------------- 1 | export class OpenTypeParser { 2 | 3 | public static openType: any; 4 | 5 | public static parseData(tag: any) { 6 | const opentype = OpenTypeParser.openType; 7 | if (!opentype) 8 | console.error('[OpenTypeParser] - opentype.js is not registered'); 9 | 10 | const font = opentype.parse(tag.data.buffer); 11 | return font; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/bitmap.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ImageType, roundToMultipleOfFour, Inflate } from '@awayjs/graphics'; 18 | import { ImageDefinition } from './image'; 19 | //import notImplemented = Shumway.Debug.notImplemented; 20 | //import assertUnreachable = Shumway.Debug.assertUnreachable; 21 | //import roundToMultipleOfFour = Shumway.IntegerUtilities.roundToMultipleOfFour; 22 | 23 | import { BitmapTag } from '../../../factories/base/SWFTags'; 24 | 25 | export const enum BitmapFormat { 26 | /** 27 | * 8-bit color mapped image. 28 | */ 29 | FORMAT_COLORMAPPED = 3, 30 | 31 | /** 32 | * 15-bit RGB image. 33 | */ 34 | FORMAT_15BPP = 4, 35 | 36 | /** 37 | * 24-bit RGB image, however stored as 4 byte value 0x00RRGGBB. 38 | */ 39 | FORMAT_24BPP = 5 40 | } 41 | 42 | /* 43 | * Returns a Uint8ClampedArray of RGBA values. The source image is color mapped meaning 44 | * that the buffer is first prefixed with a color table: 45 | * 46 | * +--------------|--------------------------------------------------+ 47 | * | Color Table | Image Data (byte indices into the color table) | 48 | * +--------------|--------------------------------------------------+ 49 | * 50 | * Color Table entries are either in RGB or RGBA format. 51 | * 52 | * There are two variations of these file formats, with or without alpha. 53 | * 54 | * Row pixels always start at 32 bit alinged offsets, the color table as 55 | * well as the end of each row may be padded so that the next row of pixels 56 | * is aligned. 57 | */ 58 | function parseColorMapped(tag: BitmapTag): Uint8ClampedArray { 59 | const width = tag.width, height = tag.height; 60 | const hasAlpha = tag.hasAlpha; 61 | 62 | const padding = roundToMultipleOfFour(width) - width; 63 | const colorTableLength = tag.colorTableSize + 1; 64 | const colorTableEntrySize = hasAlpha ? 4 : 3; 65 | const colorTableSize = colorTableLength * colorTableEntrySize; 66 | const outputDataSize = colorTableSize + ((width + padding) * height); 67 | 68 | const bytes: Uint8Array = Inflate.inflate(tag.bmpData, outputDataSize, true); 69 | const view = new Uint32Array(width * height); 70 | let p = colorTableSize, i = 0, offset = 0; 71 | 72 | if (hasAlpha) { 73 | for (let y = 0; y < height; y++) { 74 | for (let x = 0; x < width; x++) { 75 | offset = bytes[p++] << 2; 76 | 77 | const a = bytes[offset + 3]; 78 | const f = a / 0xff; 79 | 80 | // doing PMA 81 | view[i++] = ( 82 | a << 24 83 | | (bytes[offset + 2] * f | 0) << 16 84 | | (bytes[offset + 1] * f | 0) << 8 85 | | (bytes[offset + 0] * f | 0)) >>> 0; 86 | } 87 | p += padding; 88 | } 89 | } else { 90 | for (let y = 0; y < height; y++) { 91 | for (let x = 0; x < width; x++) { 92 | offset = bytes[p++] * colorTableEntrySize; 93 | 94 | view[i++] = ( 95 | 0xff << 24 96 | | bytes[offset + 2] << 16 97 | | bytes[offset + 1] << 8 98 | | bytes[offset + 0]) >>> 0; 99 | } 100 | p += padding; 101 | } 102 | } 103 | 104 | return new Uint8ClampedArray(view.buffer); 105 | } 106 | 107 | /** 108 | * Returns a Uint8ClampedArray of RGBA values. 109 | */ 110 | function parse24BPP(tag: BitmapTag): Uint8ClampedArray { 111 | const width = tag.width; 112 | const height = tag.height; 113 | // Even without alpha, 24BPP is stored as 4 bytes, probably for alignment reasons. 114 | const dataSize = height * width * 4; 115 | const bytes = Inflate.inflate(tag.bmpData, dataSize, true); 116 | 117 | //bytes are in ARGB format, so we need to convert to RGBA 118 | const view = new Uint32Array(bytes.buffer); 119 | 120 | // in => PMA ARGB, out => NPMA RGBA 121 | // ? 122 | // Unpremultiply 123 | // ex: this is wrong, because when we devide on alpha PMA - we already miss a data 124 | // you should use it as PMA 125 | for (let i = 0, l = view.length; i < l; i++) { 126 | 127 | const c = view[i]; 128 | const a = c & 0xff; // A 129 | const b = ((c >> 24) & 0xff); // B 130 | const g = ((c >> 16) & 0xff); // G 131 | const r = ((c >> 8) & 0xff); // R 132 | 133 | view[i] = (a << 24 | b << 16 | g << 8 | r) >>> 0; 134 | } 135 | 136 | //assert (p * 4 === dataSize, "We should be at the end of the data buffer now."); 137 | return new Uint8ClampedArray(bytes.buffer); 138 | } 139 | 140 | function parse15BPP(tag: BitmapTag): Uint8ClampedArray { 141 | console.log('parse15BPP'); 142 | //notImplemented("parse15BPP"); 143 | /* 144 | case FORMAT_15BPP: 145 | var colorType = 0x02; 146 | var bytesPerLine = ((width * 2) + 3) & ~3; 147 | var stream = createInflatedStream(bmpData, bytesPerLine * height); 148 | 149 | for (var y = 0, i = 0; y < height; ++y) { 150 | stream.ensure(bytesPerLine); 151 | for (var x = 0; x < width; ++x, i += 4) { 152 | var word = stream.readUi16(); 153 | // Extracting RGB color components and changing values range 154 | // from 0..31 to 0..255. 155 | data[i] = 0 | (FACTOR_5BBP * ((word >> 10) & 0x1f)); 156 | data[i + 1] = 0 | (FACTOR_5BBP * ((word >> 5) & 0x1f)); 157 | data[i + 2] = 0 | (FACTOR_5BBP * (word & 0x1f)); 158 | data[i + 3] = 255; 159 | } 160 | stream += bytesPerLine; 161 | } 162 | break; 163 | */ 164 | return null; 165 | } 166 | 167 | export function defineBitmap(tag: BitmapTag): {definition: ImageDefinition; type: string, id: number} { 168 | // enterTimeline("defineBitmap"); 169 | let type = ImageType.None; 170 | let parserMethod: (tag) => Uint8ClampedArray; 171 | 172 | switch (tag.format) { 173 | case BitmapFormat.FORMAT_COLORMAPPED: 174 | parserMethod = parseColorMapped; 175 | type = ImageType.PremultipliedAlphaARGB; 176 | break; 177 | case BitmapFormat.FORMAT_24BPP: 178 | parserMethod = parse24BPP; 179 | type = ImageType.PremultipliedAlphaARGB; 180 | break; 181 | case BitmapFormat.FORMAT_15BPP: 182 | parserMethod = parse15BPP; 183 | type = ImageType.PremultipliedAlphaARGB; 184 | break; 185 | default: 186 | console.log('invalid bitmap format'); 187 | } 188 | 189 | //leaveTimeline(); 190 | const def = { 191 | definition: { 192 | type: 'image', 193 | id: tag.id, 194 | width: tag.width, 195 | height: tag.height, 196 | mimeType: 'application/octet-stream', 197 | data: null, 198 | dataType: type, 199 | // insert new field to avoid require a @awayf/graphics 200 | isPMA: type === ImageType.PremultipliedAlphaARGB, 201 | image: null 202 | }, 203 | needParse: true, 204 | lazyParser () { 205 | this.needParse = false; 206 | this.definition.data = parserMethod(tag); 207 | 208 | this.lazyParser = () => this; 209 | return def; 210 | }, 211 | type: 'image', 212 | id: tag.id, 213 | }; 214 | 215 | return def; 216 | } 217 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/button.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ButtonTag, PlaceObjectFlags, ButtonCharacterFlags, SwfTagCode } from '../../../factories/base/SWFTags'; 18 | 19 | export function defineButton(tag: ButtonTag, dictionary: any): any { 20 | const characters = tag.characters; 21 | const states = { 22 | up: [], 23 | over: [], 24 | down: [], 25 | hitTest: [] 26 | }; 27 | let i = 0, character; 28 | while ((character = characters[i++])) { 29 | const characterItem = dictionary[character.symbolId]; 30 | // The Flash Player ignores references to undefined symbols here. So should we. 31 | // TODO: What should happen if the symbol gets defined later in the file? 32 | if (characterItem) { 33 | const cmd = { 34 | symbolId: characterItem.id, 35 | code: SwfTagCode.CODE_PLACE_OBJECT, 36 | depth: character.depth, 37 | flags: 0, 38 | matrix:null, 39 | cxform:null, 40 | blendMode: null, 41 | filters: null 42 | }; 43 | 44 | if (character.filters) { 45 | cmd.flags |= PlaceObjectFlags.HasFilterList; 46 | cmd.filters = character.filters; 47 | } 48 | 49 | if (character.blendMode) { 50 | cmd.flags |= PlaceObjectFlags.HasBlendMode; 51 | cmd.blendMode = character.blendMode; 52 | } 53 | 54 | if (character.matrix) { 55 | cmd.flags |= PlaceObjectFlags.HasMatrix; 56 | cmd.matrix = character.matrix; 57 | } 58 | 59 | if (character.cxform) { 60 | cmd.flags |= PlaceObjectFlags.HasColorTransform; 61 | cmd.cxform = character.cxform; 62 | } 63 | 64 | if (character.flags & ButtonCharacterFlags.StateUp) 65 | states.up.push(cmd); 66 | if (character.flags & ButtonCharacterFlags.StateOver) 67 | states.over.push(cmd); 68 | if (character.flags & ButtonCharacterFlags.StateDown) 69 | states.down.push(cmd); 70 | if (character.flags & ButtonCharacterFlags.StateHitTest) 71 | states.hitTest.push(cmd); 72 | } else { 73 | console.log('undefined character in button ' + tag.id); 74 | } 75 | } 76 | const button = { 77 | type: 'button', 78 | id: tag.id, 79 | buttonActions: tag.buttonActions, 80 | states: states 81 | }; 82 | return button; 83 | } 84 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/image.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ImageType, Inflate } from '@awayjs/graphics'; 18 | import { ImageTag } from '../../../factories/base/SWFTags'; 19 | 20 | /** 21 | * Reads the next two bytes at the specified position. 22 | */ 23 | function readUint16(bytes: Uint8Array, position: number) { 24 | return (bytes[position] << 8) | bytes[position + 1]; 25 | } 26 | 27 | /** 28 | * Reads the next four bytes at the specified position. 29 | */ 30 | function readInt32(bytes: Uint8Array, position: number) { 31 | return (bytes[position] << 24) | (bytes[position + 1] << 16) | 32 | (bytes[position + 2] << 8) | bytes[position + 3]; 33 | } 34 | 35 | /** 36 | * Parses JPEG chunks and reads image width and height information. JPEG data 37 | * in SWFs is encoded in chunks and not directly decodable by the JPEG parser. 38 | */ 39 | export function parseJpegChunks(bytes: Uint8Array, 40 | chunks: Uint8Array[]): void { 41 | let i = 0; 42 | const n = bytes.length; 43 | // Finding first marker, and skipping the data before this marker. 44 | // (FF 00 - code is escaped FF; FF FF ... (FF xx) - fill bytes before marker). 45 | while (i < n && (bytes[i] !== 0xff || 46 | (i + 1 < n && (bytes[i + 1] === 0x00 || bytes[i + 1] === 0xff)))) { 47 | ++i; 48 | } 49 | if (i >= n) { 50 | return; // no valid data was found 51 | } 52 | 53 | do { 54 | //release || Debug.assert(bytes[i] === 0xff); 55 | const begin = i++; 56 | const code = bytes[i++]; 57 | 58 | // Some tags have length field -- using it 59 | if ((code >= 0xc0 && code <= 0xc7) || (code >= 0xc9 && code <= 0xcf) || 60 | (code >= 0xda && code <= 0xef) || code === 0xfe) { 61 | const length = readUint16(bytes, i); 62 | i += length; 63 | } 64 | 65 | // Finding next marker. 66 | while (i < n && (bytes[i] !== 0xff || 67 | (i + 1 < n && (bytes[i + 1] === 0x00 || bytes[i + 1] === 0xff)))) { 68 | ++i; 69 | } 70 | 71 | if (code === 0xd8 || code === 0xd9) { 72 | // Removing SOI and EOI to avoid wrong EOI-SOI pairs in the middle. 73 | continue; 74 | } 75 | chunks.push(bytes.subarray(begin, i)); 76 | } while (i < n); 77 | } 78 | 79 | /** 80 | * Extracts PNG width and height information. 81 | */ 82 | export function parsePngHeaders(image: any, bytes: Uint8Array): void { 83 | const ihdrOffset = 12; 84 | if (bytes[ihdrOffset] !== 0x49 || bytes[ihdrOffset + 1] !== 0x48 || 85 | bytes[ihdrOffset + 2] !== 0x44 || bytes[ihdrOffset + 3] !== 0x52) { 86 | return; 87 | } 88 | image.width = readInt32(bytes, ihdrOffset + 4); 89 | image.height = readInt32(bytes, ihdrOffset + 8); 90 | const type = bytes[ihdrOffset + 14]; 91 | image.hasAlpha = type === 4 || type === 6; 92 | } 93 | 94 | /** 95 | * Joins all the chunks in a larger byte array. 96 | */ 97 | function joinChunks(chunks: Uint8Array []): Uint8Array { 98 | let length = 0; 99 | for (let i = 0; i < chunks.length; i++) { 100 | length += chunks[i].length; 101 | } 102 | const bytes = new Uint8Array(length); 103 | let offset = 0; 104 | for (let i = 0; i < chunks.length; i++) { 105 | const chunk = chunks[i]; 106 | bytes.set(chunk, offset); 107 | offset += chunk.length; 108 | } 109 | return bytes; 110 | } 111 | 112 | export interface ImageDefinition { 113 | type: string; 114 | id: number; 115 | width: number; 116 | height: number; 117 | mimeType: string; 118 | data: Uint8ClampedArray; 119 | dataType?: ImageType; 120 | image: any; // For some reason, tsc doesn't like us using the DOM Image definition here. 121 | } 122 | 123 | export interface JPEGTablesState { 124 | data: Uint8Array; 125 | parsedChunks?: Uint8Array[]; // Cached parsing results 126 | } 127 | 128 | function injectJPEGTables(chunks: Uint8Array[], state: JPEGTablesState): void { 129 | if (!state.parsedChunks) { 130 | const parsedChunks: Uint8Array[] = []; 131 | parseJpegChunks(state.data, parsedChunks); 132 | state.parsedChunks = parsedChunks; 133 | } 134 | // Finding first SOF and inserting tables there 135 | let i = 0; 136 | while (i < chunks.length && 137 | !(chunks[i][1] >= 0xc0 && chunks[i][1] <= 0xc0)) { 138 | i++; 139 | } 140 | Array.prototype.splice.apply(chunks, 141 | Array.prototype.concat.call([i, 0], state.parsedChunks)); 142 | } 143 | 144 | const JPEG_SOI = new Uint8Array([0xff, 0xd8]); 145 | const JPEG_EOI = new Uint8Array([0xff, 0xd9]); 146 | 147 | export function defineImage(tag: ImageTag): ImageDefinition { 148 | //enterTimeline("defineImage"); 149 | const image: any = { 150 | type: 'image', 151 | id: tag.id, 152 | mimeType: tag.mimeType 153 | }; 154 | const imgData = tag.imgData; 155 | 156 | if (tag.mimeType === 'image/jpeg') { 157 | // Parsing/repairing the SWF JPEG data. 158 | const chunks: Uint8Array[] = []; 159 | chunks.push(JPEG_SOI); 160 | parseJpegChunks(imgData, chunks); 161 | if (tag.jpegTables) { 162 | injectJPEGTables(chunks, tag.jpegTables); 163 | } 164 | chunks.push(JPEG_EOI); 165 | // Finding SOF to extract image size. 166 | chunks.forEach(function (chunk: Uint8Array) { 167 | const code = chunk[1]; 168 | if (code >= 0xc0 && code <= 0xc3) { 169 | image.height = readUint16(chunk, 5); 170 | image.width = readUint16(chunk, 7); 171 | } 172 | }); 173 | 174 | image.data = joinChunks(chunks); 175 | image.dataType = ImageType.JPEG; 176 | 177 | const alphaData: Uint8Array = tag.alphaData; 178 | if (alphaData) { 179 | const length = image.width * image.height; 180 | try { 181 | image.alphaData = Inflate.inflate(alphaData, length, true); 182 | } catch (e) { 183 | // Alpha layer is invalid, so hiding everything. 184 | image.alphaData = new Uint8Array(length); 185 | } 186 | } 187 | } else { 188 | parsePngHeaders(image, imgData); 189 | image.data = imgData; 190 | image.dataType = ImageType.PNG; 191 | } 192 | //leaveTimeline(); 193 | return image; 194 | } 195 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/label.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { StaticTextTag } from '../../../factories/base/SWFTags'; 17 | 18 | export function defineLabel(tag: StaticTextTag): any { 19 | const label = { 20 | type: 'label', 21 | id: tag.id, 22 | fillBounds: tag.bbox, 23 | matrix: tag.matrix, 24 | tag: { 25 | hasText: true, 26 | initialText: '', 27 | html: true, 28 | readonly: true 29 | }, 30 | records: tag.records, 31 | coords: null, 32 | static: true, 33 | require: null 34 | }; 35 | return label; 36 | } 37 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/shape.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ShapeTag } from '@awayjs/graphics'; 18 | 19 | import { SWFParser } from '../../SWFParser'; 20 | import { ShapeFlags } from '../../../factories/base/SWFTags'; 21 | import { SYMBOL_TYPE } from '../../ISymbol'; 22 | import { ISceneGraphFactory } from '@awayjs/scene'; 23 | 24 | /* 25 | * Applies the current segment1 to the paths of all styles specified in the last 26 | * style-change record. 27 | * 28 | * For fill0, we have to apply commands and their data in reverse order, to turn 29 | * left fills into right ones. 30 | * 31 | * If we have more than one style, we only recorded commands for the first one 32 | * and have to duplicate them for the other styles. The order is: fill1, line, 33 | * fill0. (That means we only ever recorded into fill0 if that's the only style.) 34 | */ 35 | /* 36 | 37 | // 80pro: i simplified the code so that it doesnt use this function anymore. 38 | // now we directly create needed amount of segments and apply them to the pathes 39 | function applySegmentToStyles(segment1: PathSegment, styles, 40 | linePaths: SegmentedPath[], fillPaths: SegmentedPath[]) 41 | { 42 | if (!segment1) { 43 | return; 44 | } 45 | if(styles.fill0 && styles.fill1 && styles.fill0==styles.fill1){ 46 | console.log(" same fill on both sides"); 47 | return;//80pro: ignore segments with same fill on both sides 48 | } 49 | var path1: SegmentedPath; 50 | if (styles.fill0) { 51 | path1 = fillPaths[styles.fill0 - 1]; 52 | // If fill0 is the only style, we have pushed the segment1 to its stack. In 53 | // that case, just mark it as reversed and move on. 54 | if (!(styles.fill1 || styles.line)) { 55 | segment1.isReversed = true; 56 | return; 57 | } else { 58 | path1.addSegment(segment1.toReversed()); 59 | } 60 | } 61 | if (styles.line && styles.fill1) { 62 | path1 = linePaths[styles.line - 1]; 63 | path1.addSegment(segment1.clone()); 64 | } 65 | } 66 | */ 67 | 68 | export function defineShape(tag: ShapeTag, factory: ISceneGraphFactory): any { 69 | //console.log(fillPaths, linePaths); 70 | 71 | const isMorph = tag.flags & ShapeFlags.IsMorph; 72 | 73 | tag.factory = factory; 74 | (tag as any).type = isMorph ? SYMBOL_TYPE.MORPH : SYMBOL_TYPE.SHAPE; 75 | 76 | return tag; 77 | } 78 | 79 | // function writeLineStyle(style: ShapeStyle, shape: ShapeData): void { 80 | // // No scaling == 0, normal == 1, vertical only == 2, horizontal only == 3. 81 | // var scaleMode = style.noHscale ? 82 | // (style.noVscale ? 0 : 2) : 83 | // style.noVscale ? 3 : 1; 84 | // // TODO: Figure out how to handle startCapsStyle 85 | // var thickness = clamp(style.width, 0, 0xff * 20)|0; 86 | // shape.lineStyle(thickness, style.color, 87 | // style.pixelHinting, scaleMode, style.endCapsStyle, 88 | // style.jointStyle, style.miterLimit); 89 | // } 90 | 91 | // function writeMorphLineStyle(style: ShapeStyle, shape: ShapeData): void { 92 | // // TODO: Figure out how to handle startCapsStyle 93 | // var thickness = clamp(style.width, 0, 0xff * 20)|0; 94 | // shape.writeMorphLineStyle(thickness, style.color); 95 | // } 96 | 97 | // function writeGradient(command: PathCommand, style: ShapeStyle, shape: ShapeData): void { 98 | // var gradientType = style.type === FillType.LinearGradient ? 99 | // GradientType.Linear : 100 | // GradientType.Radial; 101 | // shape.beginGradient(command, style.colors, style.ratios, 102 | // gradientType, style.transform, style.spreadMethod, 103 | // style.interpolationMode, style.focalPoint / 2 | 0); 104 | // } 105 | 106 | // function writeMorphGradient(style: ShapeStyle, shape: ShapeData) { 107 | // shape.writeMorphGradient(style.colors, style.ratios, style.transform); 108 | // } 109 | 110 | // function writeBitmap(command: PathCommand, style: ShapeStyle, shape: ShapeData): void { 111 | // shape.beginBitmap(command, style.bitmapIndex, style.transform, style.repeat, style.smooth); 112 | // } 113 | 114 | // function writeMorphBitmap(style: ShapeStyle, shape: ShapeData) { 115 | // shape.writeMorphBitmap(style.transform); 116 | // } 117 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/sound.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { SoundTag } from '../../../factories/base/SWFTags'; 18 | import { release } from '../../../factories/base/utilities/Debug'; 19 | 20 | const SOUND_SIZE_8_BIT = 0; 21 | const SOUND_SIZE_16_BIT = 1; 22 | const SOUND_TYPE_MONO = 0; 23 | const SOUND_TYPE_STEREO = 1; 24 | 25 | const SOUND_FORMAT_PCM_BE = 0; 26 | const SOUND_FORMAT_ADPCM = 1; 27 | const SOUND_FORMAT_MP3 = 2; 28 | const SOUND_FORMAT_PCM_LE = 3; 29 | const SOUND_FORMAT_NELLYMOSER_16 = 4; 30 | const SOUND_FORMAT_NELLYMOSER_8 = 5; 31 | const SOUND_FORMAT_NELLYMOSER = 6; 32 | const SOUND_FORMAT_SPEEX = 11; 33 | 34 | const SOUND_RATES = [5512, 11025, 22050, 44100]; 35 | 36 | const WaveHeader = new Uint8Array([0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 37 | 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 38 | 0x01, 0x00, 0x02, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x10, 0xB1, 0x02, 0x00, 39 | 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00]); 40 | 41 | export function packageWave(data, sampleRate, channels, size, swapBytes) { 42 | const sizeInBytes = size >> 3; 43 | const sizePerSecond = channels * sampleRate * sizeInBytes; 44 | const sizePerSample = channels * sizeInBytes; 45 | const dataLength = data.length + (data.length & 1); 46 | const buffer = new ArrayBuffer(WaveHeader.length + dataLength); 47 | const bytes = new Uint8Array(buffer); 48 | bytes.set(WaveHeader); 49 | if (swapBytes) { 50 | for (let i = 0, j = WaveHeader.length; i < data.length; i += 2, j += 2) { 51 | bytes[j] = data[i + 1]; 52 | bytes[j + 1] = data[i]; 53 | } 54 | } else { 55 | bytes.set(data, WaveHeader.length); 56 | } 57 | const view = new DataView(buffer); 58 | view.setUint32(4, dataLength + 36, true); 59 | view.setUint16(22, channels, true); 60 | view.setUint32(24, sampleRate, true); 61 | view.setUint32(28, sizePerSecond, true); 62 | view.setUint16(32, sizePerSample, true); 63 | view.setUint16(34, size, true); 64 | view.setUint32(40, dataLength, true); 65 | return { 66 | data: bytes, 67 | mimeType: 'audio/wav' 68 | }; 69 | } 70 | 71 | export function defineSound(tag: SoundTag) { 72 | const channels = tag.soundType == SOUND_TYPE_STEREO ? 2 : 1; 73 | const samplesCount = tag.samplesCount; 74 | const sampleRate = SOUND_RATES[tag.soundRate]; 75 | const data = tag.soundData; 76 | 77 | let pcm: any, packaged: any; 78 | 79 | switch (tag.soundFormat) { 80 | case SOUND_FORMAT_PCM_BE: { 81 | pcm = new Float32Array(samplesCount * channels); 82 | if (tag.soundSize == SOUND_SIZE_16_BIT) { 83 | for (let i = 0, j = 0; i < pcm.length; i++, j += 2) 84 | pcm[i] = ((data[j] << 24) | (data[j + 1] << 16)) / 2147483648; 85 | packaged = packageWave(data, sampleRate, channels, 16, true); 86 | } else { 87 | for (let i = 0; i < pcm.length; i++) 88 | pcm[i] = (data[i] - 128) / 128; 89 | packaged = packageWave(data, sampleRate, channels, 8, false); 90 | } 91 | break; 92 | } 93 | case SOUND_FORMAT_PCM_LE: { 94 | pcm = new Float32Array(samplesCount * channels); 95 | if (tag.soundSize == SOUND_SIZE_16_BIT) { 96 | for (let i = 0, j = 0; i < pcm.length; i++, j += 2) 97 | pcm[i] = ((data[j + 1] << 24) | (data[j] << 16)) / 2147483648; 98 | packaged = packageWave(data, sampleRate, channels, 16, false); 99 | } else { 100 | for (let i = 0; i < pcm.length; i++) 101 | pcm[i] = (data[i] - 128) / 128; 102 | packaged = packageWave(data, sampleRate, channels, 8, false); 103 | } 104 | break; 105 | } 106 | case SOUND_FORMAT_MP3: 107 | packaged = { 108 | seek: data[0] | (data[1] << 8), 109 | data: new Uint8Array(data.subarray(2)), 110 | mimeType: 'audio/mpeg' 111 | }; 112 | break; 113 | case SOUND_FORMAT_ADPCM: { 114 | const pcm16 = new Int16Array(samplesCount * channels); 115 | decodeACPCMSoundData(data, pcm16, channels); 116 | pcm = new Float32Array(samplesCount * channels); 117 | 118 | for (let i = 0; i < pcm.length; i++) { 119 | pcm[i] = pcm16[i] / 32768; 120 | } 121 | 122 | packaged = packageWave(new Uint8Array(pcm16.buffer), sampleRate, channels, 123 | 16, !(new Uint8Array(new Uint16Array([1]).buffer))[0]); 124 | break; 125 | } 126 | default: 127 | release || console.log('Unsupported audio format: ' + tag.soundFormat); 128 | } 129 | 130 | const sound = { 131 | type: 'sound', 132 | id: tag.id, 133 | sampleRate: sampleRate, 134 | samplesCount: samplesCount, 135 | channels: channels, 136 | pcm: pcm, 137 | packaged: null 138 | }; 139 | if (packaged) { 140 | sound.packaged = packaged; 141 | } 142 | return sound; 143 | } 144 | 145 | const ACPCMIndexTables = [ 146 | [-1, 2], 147 | [-1, -1, 2, 4], 148 | [-1, -1, -1, -1, 2, 4, 6, 8], 149 | [-1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16] 150 | ]; 151 | 152 | const ACPCMStepSizeTable = [ 153 | 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 154 | 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 155 | 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 156 | 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 157 | 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 158 | 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 159 | ]; 160 | 161 | function decodeACPCMSoundData(data, pcm16, channels) { 162 | function readBits(n) { 163 | while (dataBufferLength < n) { 164 | dataBuffer = (dataBuffer << 8) | data[dataPosition++]; 165 | dataBufferLength += 8; 166 | } 167 | dataBufferLength -= n; 168 | return (dataBuffer >>> dataBufferLength) & ((1 << n) - 1); 169 | } 170 | var dataPosition = 0; 171 | var dataBuffer = 0; 172 | var dataBufferLength = 0; 173 | 174 | let pcmPosition = 0; 175 | const codeSize = readBits(2); 176 | const indexTable = ACPCMIndexTables[codeSize]; 177 | while (pcmPosition < pcm16.length) { 178 | var x = pcm16[pcmPosition++] = (readBits(16) << 16) >> 16, x2; 179 | var stepIndex = readBits(6), stepIndex2; 180 | if (channels > 1) { 181 | x2 = pcm16[pcmPosition++] = (readBits(16) << 16) >> 16; 182 | stepIndex2 = readBits(6); 183 | } 184 | const signMask = 1 << (codeSize + 1); 185 | for (let i = 0; i < 4095; i++) { 186 | let nibble = readBits(codeSize + 2); 187 | let step = ACPCMStepSizeTable[stepIndex]; 188 | let sum = 0; 189 | for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) { 190 | if (nibble & currentBit) sum += step; 191 | } 192 | x += (nibble & signMask ? -1 : 1) * (sum + step); 193 | pcm16[pcmPosition++] = (x = (x < -32768 ? -32768 : x > 32767 ? 32767 : x)); 194 | stepIndex += indexTable[nibble & ~signMask]; 195 | stepIndex = stepIndex < 0 ? 0 : stepIndex > 88 ? 88 : stepIndex; 196 | if (channels > 1) { 197 | nibble = readBits(codeSize + 2); 198 | step = ACPCMStepSizeTable[stepIndex2]; 199 | sum = 0; 200 | for (var currentBit = signMask >> 1; currentBit; currentBit >>= 1, step >>= 1) { 201 | if (nibble & currentBit) sum += step; 202 | } 203 | x2 += (nibble & signMask ? -1 : 1) * (sum + step); 204 | pcm16[pcmPosition++] = (x2 = (x2 < -32768 ? -32768 : x2 > 32767 ? 32767 : x2)); 205 | stepIndex2 += indexTable[nibble & ~signMask]; 206 | stepIndex2 = stepIndex2 < 0 ? 0 : stepIndex2 > 88 ? 88 : stepIndex2; 207 | } 208 | 209 | } 210 | } 211 | } 212 | 213 | let nextSoundStreamId = 0; 214 | 215 | export interface DecodedSound { 216 | streamId: number; 217 | samplesCount: number; 218 | pcm?: Float32Array; 219 | data?: Uint8Array; 220 | seek?: number; 221 | } 222 | 223 | export class SoundStream { 224 | streamId: number; 225 | samplesCount: number; 226 | sampleRate: number; 227 | channels: number; 228 | streamSize: number; 229 | format: any; 230 | currentSample: number; 231 | allChunks: Uint8Array[]; 232 | 233 | decode: (block: Uint8Array) => DecodedSound; 234 | 235 | constructor(samplesCount, sampleRate, channels, streamSize) { 236 | this.streamId = (nextSoundStreamId++); 237 | this.samplesCount = samplesCount; 238 | this.sampleRate = sampleRate; 239 | this.channels = channels; 240 | this.streamSize = streamSize; 241 | this.format = null; 242 | this.currentSample = 0; 243 | this.allChunks = []; 244 | } 245 | 246 | static FromTag(tag): SoundStream { 247 | const channels = tag.streamType == SOUND_TYPE_STEREO ? 2 : 1; 248 | const samplesCount = tag.samplesCount; 249 | const sampleRate = SOUND_RATES[tag.streamRate]; 250 | const streamSize = tag.streamSize; 251 | const stream = new SoundStream(samplesCount, sampleRate, channels, streamSize); 252 | 253 | switch (tag.streamCompression) { 254 | case SOUND_FORMAT_PCM_BE: 255 | case SOUND_FORMAT_PCM_LE: 256 | stream.format = 'wave'; 257 | if (tag.soundSize == SOUND_SIZE_16_BIT) { 258 | stream.decode = tag.streamCompression === SOUND_FORMAT_PCM_BE ? 259 | SwfSoundStream_decode_PCM_be : 260 | SwfSoundStream_decode_PCM_le; 261 | } else { 262 | stream.decode = SwfSoundStream_decode_PCM; 263 | } 264 | break; 265 | case SOUND_FORMAT_MP3: 266 | stream.format = 'mp3'; 267 | stream.decode = SwfSoundStream_decode_MP3; 268 | break; 269 | default: 270 | release || console.log('Unsupported audio stream format: ' + tag.streamCompression); 271 | return null; 272 | } 273 | 274 | return stream; 275 | } 276 | } 277 | 278 | function SwfSoundStream_decode_PCM(data): DecodedSound { 279 | const pcm = new Float32Array(data.length); 280 | for (let i = 0; i < pcm.length; i++) 281 | pcm[i] = (data[i] - 128) / 128; 282 | this.currentSample += pcm.length / this.channels; 283 | return { 284 | streamId: this.streamId, 285 | samplesCount: pcm.length / this.channels, 286 | pcm: pcm, 287 | data: data, 288 | seek:0 289 | }; 290 | } 291 | 292 | function SwfSoundStream_decode_PCM_be(data): DecodedSound { 293 | const pcm = new Float32Array(data.length / 2); 294 | for (let i = 0, j = 0; i < pcm.length; i++, j += 2) 295 | pcm[i] = ((data[j] << 24) | (data[j + 1] << 16)) / 2147483648; 296 | this.currentSample += pcm.length / this.channels; 297 | return { 298 | streamId: this.streamId, 299 | samplesCount: pcm.length / this.channels, 300 | pcm: pcm, 301 | data: data, 302 | seek:0 303 | }; 304 | } 305 | 306 | function SwfSoundStream_decode_PCM_le(data): DecodedSound { 307 | const pcm = new Float32Array(data.length / 2); 308 | for (let i = 0, j = 0; i < pcm.length; i++, j += 2) 309 | pcm[i] = ((data[j + 1] << 24) | (data[j] << 16)) / 2147483648; 310 | this.currentSample += pcm.length / this.channels; 311 | return { 312 | streamId: this.streamId, 313 | samplesCount: pcm.length / this.channels, 314 | pcm: pcm, 315 | data: data, 316 | seek:0 317 | }; 318 | } 319 | 320 | function SwfSoundStream_decode_MP3(data): DecodedSound { 321 | const samplesCount = (data[1] << 8) | data[0]; 322 | const seek = (data[3] << 8) | data[2]; 323 | this.currentSample += samplesCount; 324 | return { 325 | streamId: this.streamId, 326 | samplesCount: samplesCount, 327 | data: new Uint8Array(data.subarray(4)), 328 | seek: seek 329 | }; 330 | } 331 | -------------------------------------------------------------------------------- /lib/parsers/utils/parser/text.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2014 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { TextTag } from '../../../factories/base/SWFTags'; 18 | 19 | export function defineText(tag: TextTag): any { 20 | const bold = false; 21 | const italic = false; 22 | 23 | return { 24 | type: 'text', 25 | id: tag.id, 26 | fillBounds: tag.bbox, 27 | variableName: tag.variableName, // for AVM1 28 | tag: tag, 29 | bold: bold, 30 | italic: italic 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /lib/parsers/utils/stream.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Mozilla Foundation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import { utf8encode } from '@awayjs/graphics'; 17 | 18 | export var StreamNoDataError = {}; 19 | 20 | const masks = new Uint32Array(33); 21 | for (let i = 1, mask = 0; i <= 32; ++i) { 22 | masks[i] = mask = (mask << 1) | 1; 23 | } 24 | 25 | export class Stream { 26 | bytes: Uint8Array; 27 | view: DataView; 28 | pos: number; 29 | end: number; 30 | 31 | bitBuffer: number; 32 | bitLength: number; 33 | 34 | constructor(buffer, offset?: number, length?: number, maxLength?: number) { 35 | if (offset === undefined) 36 | offset = 0; 37 | if (buffer.buffer instanceof ArrayBuffer) { 38 | offset += buffer.byteOffset; 39 | buffer = buffer.buffer; 40 | } 41 | if (length === undefined) 42 | length = buffer.byteLength - offset; 43 | if (maxLength === undefined) 44 | maxLength = length; 45 | 46 | this.bytes = new Uint8Array(buffer, offset, maxLength); 47 | this.view = new DataView(buffer, offset, maxLength); 48 | this.pos = 0; 49 | this.end = length; 50 | 51 | this.bitBuffer = 0; 52 | this.bitLength = 0; 53 | } 54 | 55 | align() { 56 | this.bitBuffer = this.bitLength = 0; 57 | } 58 | 59 | ensure(size: number) { 60 | if (this.pos + size > this.end) { 61 | throw StreamNoDataError; 62 | } 63 | } 64 | 65 | remaining(): number { 66 | return this.end - this.pos; 67 | } 68 | 69 | substream(begin: number, end: number): Stream { 70 | const stream = new Stream(this.bytes); 71 | stream.pos = begin; 72 | stream.end = end; 73 | return stream; 74 | } 75 | 76 | push(data) { 77 | const bytes = this.bytes; 78 | const newBytesLength = this.end + data.length; 79 | if (newBytesLength > bytes.length) { 80 | throw 'stream buffer overfow'; 81 | } 82 | bytes.set(data, this.end); 83 | this.end = newBytesLength; 84 | } 85 | 86 | readSi8(): number { 87 | return this.view.getInt8(this.pos++); 88 | } 89 | 90 | readSi16(): number { 91 | const r = this.view.getInt16(this.pos, true); 92 | this.pos += 2; 93 | return r; 94 | } 95 | 96 | readSi32(): number { 97 | const r = this.view.getInt32(this.pos, true); 98 | this.pos += 4; 99 | return r; 100 | } 101 | 102 | readUi8(): number { 103 | return this.bytes[this.pos++]; 104 | } 105 | 106 | readUi16(): number { 107 | const r = this.view.getUint16(this.pos, true); 108 | this.pos += 2; 109 | return r; 110 | } 111 | 112 | readUi32(): number { 113 | const r = this.view.getUint32(this.pos, true); 114 | this.pos += 4; 115 | return r; 116 | } 117 | 118 | readFixed(): number { 119 | const r = this.view.getInt32(this.pos, true) / 65536; 120 | this.pos += 4; 121 | return r; 122 | } 123 | 124 | readFixed8(): number { 125 | const r = this.view.getInt16(this.pos, true) / 256; 126 | this.pos += 2; 127 | return r; 128 | } 129 | 130 | readFloat16(): number { 131 | const ui16 = this.view.getUint16(this.pos, false); 132 | this.pos += 2; 133 | const sign = ui16 >> 15 ? -1 : 1; 134 | const exponent = (ui16 & 0x7c00) >> 10; 135 | const fraction = ui16 & 0x03ff; 136 | if (!exponent) 137 | return sign * Math.pow(2, -14) * (fraction / 1024); 138 | if (exponent === 0x1f) 139 | return fraction ? NaN : sign * Infinity; 140 | return sign * Math.pow(2, exponent - 15) * (1 + (fraction / 1024)); 141 | } 142 | 143 | readFloat(): number { 144 | const r = this.view.getFloat32(this.pos, true); 145 | this.pos += 4; 146 | return r; 147 | } 148 | 149 | readDouble(): number { 150 | const r = this.view.getFloat64(this.pos, true); 151 | this.pos += 8; 152 | return r; 153 | } 154 | 155 | readEncodedU32(): number { 156 | const bytes = this.bytes; 157 | let val = bytes[this.pos++]; 158 | if (!(val & 0x080)) 159 | return val; 160 | val = (val & 0x7f) | bytes[this.pos++] << 7; 161 | if (!(val & 0x4000)) 162 | return val; 163 | val = (val & 0x3fff) | bytes[this.pos++] << 14; 164 | if (!(val & 0x200000)) 165 | return val; 166 | val = (val & 0x1FFFFF) | bytes[this.pos++] << 21; 167 | if (!(val & 0x10000000)) 168 | return val; 169 | return (val & 0xFFFFFFF) | (bytes[this.pos++] << 28); 170 | } 171 | 172 | readBool(): boolean { 173 | return !!this.bytes[this.pos++]; 174 | } 175 | 176 | readSb(size: number): number { 177 | return (this.readUb(size) << (32 - size)) >> (32 - size); 178 | } 179 | 180 | readUb(size: number): number { 181 | let buffer = this.bitBuffer; 182 | let bitlen = this.bitLength; 183 | let val = 0; 184 | while (size > bitlen) { 185 | if (bitlen > 24) { 186 | // Avoid overflow. Save current buffer in val and add remaining bits later. 187 | size -= bitlen; 188 | val = buffer << size; 189 | bitlen = 0; 190 | } 191 | buffer = (buffer << 8) | this.bytes[this.pos++]; 192 | bitlen += 8; 193 | } 194 | bitlen -= size; 195 | val |= (buffer >>> bitlen) & masks[size]; 196 | this.bitBuffer = buffer; 197 | this.bitLength = bitlen; 198 | return val; 199 | } 200 | 201 | readFb(size: number): number { 202 | return this.readSb(size) / 65536; 203 | } 204 | 205 | readString(length: number): string { 206 | const bytes = this.bytes; 207 | let codes: Uint8Array; 208 | let pos = this.pos; 209 | if (length > -1) { 210 | codes = bytes.subarray(pos, pos += length); 211 | } else { 212 | length = 0; 213 | for (let i = pos; bytes[i]; i++) { 214 | length++; 215 | } 216 | codes = bytes.subarray(pos, pos += length); 217 | pos++; 218 | } 219 | this.pos = pos; 220 | let str = utf8encode(codes); 221 | if (str.indexOf('\0') >= 0) { 222 | str = str.split('\0').join(''); 223 | } 224 | return str; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /lib/redirectResolver.ts: -------------------------------------------------------------------------------- 1 | 2 | type TLoaderRuleFunc = (url: string) => boolean; 3 | type TLoaderRuleFuncResult = (url: string) => string; 4 | 5 | /** 6 | * Global rulles for URLLoader and Loader instances, applyed before local rulles 7 | */ 8 | export const globalRedirectRules: IRedirectRule[] = []; 9 | 10 | export interface IRedirectRule { 11 | test: string | RegExp | TLoaderRuleFunc | undefined, 12 | resolve?: string | RegExp| TLoaderRuleFuncResult | undefined, 13 | supressErrors?: boolean, 14 | supressLoad?: boolean, 15 | } 16 | 17 | export function matchRedirect(url: string, rules?: Array): {url: string, supressErrors: boolean, supressLoad: boolean} | undefined { 18 | 19 | let rule: { 20 | url: string, supressErrors: boolean, supressLoad: boolean 21 | } = undefined; 22 | 23 | const all = rules ? globalRedirectRules.concat(rules) : globalRedirectRules; 24 | 25 | all.forEach(({ test, resolve, supressErrors = false, supressLoad = false })=>{ 26 | let passed: boolean | string = false; 27 | 28 | if (typeof test === 'function') { 29 | passed = test(url); 30 | } else if (test instanceof RegExp) { 31 | passed = test.test(url); 32 | } else if (typeof test === 'string') { 33 | passed = test === url; 34 | } 35 | 36 | if (passed) { 37 | if (rule) { 38 | console.warn('[LOADER] Duplicate redirect rules, latest rule would be used!'); 39 | } 40 | 41 | rule = { 42 | url, supressErrors : supressErrors, supressLoad : supressLoad 43 | }; 44 | 45 | if (typeof resolve === 'function') { 46 | rule.url = resolve(url); 47 | } else if (resolve instanceof RegExp) { 48 | rule.url = url.match(resolve)[0]; 49 | } else if (typeof resolve === 'string') { 50 | rule.url = resolve; 51 | } else if (typeof passed === 'string') { 52 | rule.url = passed; 53 | } 54 | 55 | if (typeof rule.url === 'undefined') { 56 | console.warn('[LOADER] Redirect url is null, would be used original url!'); 57 | rule.url = url; 58 | } 59 | } 60 | }); 61 | 62 | return rule; 63 | } -------------------------------------------------------------------------------- /lib/stat/Stat.ts: -------------------------------------------------------------------------------- 1 | 2 | const enum RECORD_STATE { 3 | NONE, BEGIN, END, DROP 4 | } 5 | export class Record { 6 | private _records: {[key: string]: Record} = {}; 7 | private _startTime: number = 0; 8 | private _endTime: number = 0; 9 | private _subs: Record[] = []; 10 | private _state: RECORD_STATE = RECORD_STATE.NONE; 11 | 12 | constructor(public name: string, private _subrecord = false) {} 13 | 14 | public rec(name: string, subrecrod = false): Record { 15 | if (this._subrecord) { 16 | throw 'Suprecord can\'t support nested records!'; 17 | } 18 | let sub = this._subs[this._subs.length - 1]; 19 | if (!(sub && sub._state === RECORD_STATE.BEGIN)) { 20 | sub = this; 21 | } 22 | 23 | return sub._records[name] || (sub._records[name] = new Record(name, subrecrod)); 24 | } 25 | 26 | public begin() { 27 | if (this._state !== RECORD_STATE.NONE) { 28 | return; 29 | } 30 | 31 | if (!this._subrecord) { 32 | 33 | if (this._subs[this._subs.length - 1]) { 34 | this._subs[this._subs.length - 1].end(); 35 | } 36 | 37 | const sub = this.rec(this.name + '_' + this._subs.length, true); 38 | this._subs.push(sub); 39 | 40 | return sub.begin(); 41 | } 42 | 43 | this._state = RECORD_STATE.BEGIN; 44 | return this._startTime = performance.now(); 45 | } 46 | 47 | public end() { 48 | 49 | for (const key in this._records) { 50 | this._records[key].end(); 51 | } 52 | 53 | if (this._state !== RECORD_STATE.BEGIN) { 54 | return; 55 | } 56 | 57 | if (!this._subrecord) { 58 | return this._subs[this._subs.length - 1].end(); 59 | } 60 | 61 | this._state = RECORD_STATE.END; 62 | this._endTime = performance.now(); 63 | 64 | return this._endTime - this._startTime; 65 | } 66 | 67 | public drop() { 68 | this.end(); 69 | 70 | if (!this._subrecord) { 71 | return this._subs[this._subs.length - 1].drop(); 72 | } 73 | 74 | this._state = RECORD_STATE.DROP; 75 | this._startTime = this._endTime = 0; 76 | } 77 | 78 | get startTime(): number { 79 | if (this._state == RECORD_STATE.DROP) { 80 | return 0; 81 | } 82 | 83 | let startTime = this._startTime || Number.MAX_VALUE; 84 | 85 | for (const key in this._records) { 86 | const r = this._records[key]; 87 | startTime = Math.min(startTime, r.startTime); 88 | } 89 | 90 | return startTime; 91 | } 92 | 93 | get endTime(): number { 94 | if (this._state == RECORD_STATE.DROP) { 95 | return 0; 96 | } 97 | 98 | let endTime = this._endTime || -Number.MAX_VALUE; 99 | 100 | for (const key in this._records) { 101 | const r = this._records[key]; 102 | endTime = Math.max(endTime, r.endTime); 103 | } 104 | 105 | return endTime; 106 | } 107 | 108 | get duration(): number { 109 | if (this._state == RECORD_STATE.DROP) { 110 | return 0; 111 | } 112 | 113 | const len = Object.keys(this._records).length; 114 | 115 | if (!len) { 116 | return this._endTime - this._startTime; 117 | } 118 | 119 | let duration = 0; 120 | 121 | for (const key in this._records) { 122 | duration += this._records[key].duration; 123 | } 124 | 125 | return duration; 126 | } 127 | 128 | get selfDuration() { 129 | const len = Object.keys(this._records).length; 130 | 131 | if (!len) { 132 | return this._endTime - this._startTime; 133 | } 134 | 135 | return this.endTime - this.startTime; 136 | } 137 | 138 | get density() { 139 | if (this._state == RECORD_STATE.DROP) { 140 | return 0; 141 | } 142 | 143 | return this.duration / this.selfDuration; 144 | } 145 | 146 | toString() { 147 | const selfDuration = this.selfDuration; 148 | return `Name: ${this.name}, duration: ${this.duration.toFixed(2)}ms, self: ${selfDuration.toFixed(2)}ms, density:${(this.density * 100).toFixed(0)}%`; 149 | } 150 | 151 | toTable(ident: number = 0, filter: (r: Record) => boolean) { 152 | let r = ' '.repeat(ident) + this.toString() + '\n'; 153 | 154 | if (this._subs.length === 1 && Object.keys(this._records).length === 1) { 155 | return r; 156 | } 157 | 158 | for (const key in this._records) { 159 | const rec = this._records[key]; 160 | 161 | if (rec._state === RECORD_STATE.DROP) { 162 | continue; 163 | } 164 | 165 | if (filter && filter(rec)) { 166 | continue; 167 | } 168 | 169 | r += rec.toTable(ident + 1, filter); 170 | } 171 | 172 | return r; 173 | } 174 | } 175 | 176 | export class Stat extends Record { 177 | private static _instance: Stat; 178 | constructor() { 179 | super('ROOT'); 180 | //@ts-ignore 181 | window.AWAY_ROOT_STAT = this; 182 | } 183 | 184 | static rec(name: string) { 185 | if (!this._instance) { 186 | this._instance = new Stat(); 187 | } 188 | 189 | return this._instance.rec(name); 190 | } 191 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@awayfl/swf-loader", 3 | "version": "0.4.131", 4 | "description": "Viewer for playing swf files", 5 | "main": "bundle/awayfl-swf-loader.umd.js", 6 | "module": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "url": "http://www.away3d.com", 9 | "author": "Rob Bateman", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/awayfl/swf-loader.git" 13 | }, 14 | "scripts": { 15 | "rimraf": "rimraf", 16 | "rollup": "rollup -c", 17 | "tsc": "tsc", 18 | "tsc:build": "npm run tsc || exit 0", 19 | "clean": "npm cache clean && npm run rimraf -- node_modules dist bundle", 20 | "clean:dist": "npm run rimraf -- dist bundle", 21 | "watch": "npm run tsc -- --w", 22 | "eslint": "eslint ./lib --fix", 23 | "eslint:commit": "npm run eslint && npm run --silent gitdiff || git add -u && git commit -m \"fixed by eslint\"", 24 | "gitdiff": "git diff --quiet && git diff --cached --quiet", 25 | "preversion": "npm run gitdiff && npm run eslint:commit | exit 0", 26 | "prebuild": "npm run clean:dist", 27 | "build": "npm run tsc:build && npm run rollup", 28 | "copyVersionToIndex": "node ./copyVersionToIndex && git add ./index.ts && git commit -m \"update version number in index.ts\"", 29 | "version": "npm run copyVersionToIndex && npm run build", 30 | "postversion": "git push && git push --tags && npm publish" 31 | }, 32 | "keywords": [ 33 | "AwayJS", 34 | "WebGL", 35 | "2D", 36 | "3D", 37 | "graphics" 38 | ], 39 | "license": "Apache-2.0", 40 | "bugs": { 41 | "url": "https://github.com/awayfl/swf-loader/issues" 42 | }, 43 | "homepage": "https://github.com/awayfl/swf-loader#readme", 44 | "peerDependencies": { 45 | "@awayjs/core": "^0.9.0", 46 | "@awayjs/graphics": "^0.5.0", 47 | "@awayjs/materials": "^0.6.0", 48 | "@awayjs/renderer": "^0.11.0", 49 | "@awayjs/scene": "^0.13.0", 50 | "@awayjs/stage": "^0.11.0", 51 | "tslib": "^2.3.0" 52 | }, 53 | "devDependencies": { 54 | "@awayjs/core": "^0.9.0", 55 | "@awayjs/graphics": "^0.5.0", 56 | "@awayjs/materials": "^0.6.0", 57 | "@awayjs/renderer": "^0.11.0", 58 | "@awayjs/scene": "^0.13.0", 59 | "@awayjs/stage": "^0.11.0", 60 | "@awayjs/view": "^0.6.0", 61 | "@rollup/plugin-commonjs": "^18.0.0", 62 | "@rollup/plugin-terser": "^0.4.3", 63 | "@rollup/plugin-node-resolve": "^11.2.1", 64 | "@typescript-eslint/eslint-plugin": "^4.4.1", 65 | "@typescript-eslint/parser": "^4.4.1", 66 | "eslint": "^7.11.0", 67 | "rimraf": "^2.5.2", 68 | "rollup": "^2.79.1", 69 | "tslib": "^2.3.0", 70 | "typescript": "^4.9.5" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import terser from '@rollup/plugin-terser'; 4 | 5 | export default { 6 | input: './dist/index.js', 7 | output: { 8 | name: 'AwayflSwfLoader', 9 | globals: { 10 | '@awayjs/core': 'AwayjsCore', 11 | '@awayjs/stage': 'AwayjsStage', 12 | '@awayjs/view': 'AwayjsView', 13 | '@awayjs/renderer': 'AwayjsRenderer', 14 | '@awayjs/graphics': 'AwayjsGraphics', 15 | '@awayjs/materials': 'AwayjsMaterials', 16 | '@awayjs/scene': 'AwayjsScene' 17 | }, 18 | sourcemap: true, 19 | format: 'umd', 20 | file: './bundle/awayfl-swf-loader.umd.js' 21 | }, 22 | external: [ 23 | '@awayjs/core', 24 | '@awayjs/stage', 25 | '@awayjs/view', 26 | '@awayjs/renderer', 27 | '@awayjs/graphics', 28 | '@awayjs/materials', 29 | '@awayjs/scene' 30 | ], 31 | plugins: [ 32 | nodeResolve(), 33 | commonjs(), 34 | terser(), 35 | ] 36 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "lib": ["DOM","ScriptHost", "ESNext"], 6 | "moduleResolution": "node", 7 | "noImplicitUseStrict": true, 8 | "noEmitHelpers": true, 9 | "importHelpers": true, 10 | "declaration": true, 11 | "declarationMap": true, 12 | "outDir": "./dist" 13 | }, 14 | "files": [ 15 | "./index.ts" 16 | ] 17 | } --------------------------------------------------------------------------------