├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── bin └── jbbc.js ├── compiler.js ├── decoder.js ├── dist ├── jbb-loader.js ├── jbb-loader.min.js ├── jbb.js └── jbb.min.js ├── doc ├── Bundle Format.md ├── Tutorial 1 - THREEjs │ ├── Using with THREEjs.md │ ├── files │ │ ├── crate.json │ │ └── crate.png │ └── img │ │ ├── 1-blender.png │ │ └── 2-result.png ├── bundle.json.md ├── jbb-logo.png ├── table_BUF_TYPE.png ├── table_CHUNK.png ├── table_DWS_TYPE.png ├── table_LEN.png ├── table_LN.png ├── table_NUM_TYPE.png ├── table_OP_ARR.png ├── table_OP_BULK_KNOWN.png ├── table_OP_CTL.png ├── table_OP_OBJECT.png ├── table_OP_PRIM.png ├── table_OT.png ├── table_SCALE.png └── table_S_TYPE.png ├── encoder.js ├── gulpfile.js ├── index.js ├── lib ├── BinaryBundle.js ├── BinaryStream.js ├── DecodeProfile.js ├── EncodeProfile.js ├── Errors.js └── ProgressManager.js ├── loader.js ├── package.json └── test ├── media ├── animated.jbbsrc │ ├── bundle.json │ ├── flamingo.js │ ├── horse.js │ └── monster │ │ ├── monster.jpg │ │ └── monster.js ├── heavy.jbbsrc │ ├── ben.js │ ├── ben.utf8 │ ├── ben_dds.js │ ├── bundle.json │ ├── dds │ │ ├── James_Body_Lores.dds │ │ ├── James_EyeLashBotTran.dds │ │ ├── James_EyeLashTopTran.dds │ │ ├── James_Eye_Green.dds │ │ ├── James_Eye_Inner_Green.dds │ │ ├── James_Face_Color_Hair_Lores.dds │ │ ├── James_Mouth_Gum_Lores.dds │ │ ├── James_Tongue_Lores.dds │ │ ├── MCasShoe1TEX_Lores.dds │ │ ├── MJeans1TEX_Lores.dds │ │ ├── MTshirt3TEX_Lores.dds │ │ └── Nail_Hand_01_Lores.dds │ ├── hand.jpg │ ├── hand.js │ └── hand.utf8 ├── md2.jbbsrc │ ├── bundle.json │ ├── grasslight-big.jpg │ └── ratamahatta │ │ ├── ratamahatta.md2 │ │ ├── skins │ │ ├── ctf_b.png │ │ ├── ctf_r.png │ │ ├── dead.png │ │ ├── gearwhore.png │ │ ├── ratamahatta.png │ │ └── weapon.png │ │ └── weapon.md2 ├── obj.jbbsrc │ ├── bundle.json │ └── walt │ │ ├── WaltHead_bin.bin │ │ └── WaltHead_bin.js └── vrml.jbbsrc │ ├── bundle.json │ └── house.wrl ├── simple-profile ├── objects.js ├── second-decode.js ├── second-encode.js ├── second.yaml ├── specs-decode.js ├── specs-encode.js └── specs.yaml ├── test-core.js ├── test-three.js └── utils ├── common.js ├── compare.js └── tests.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | *.local 30 | *.local.js 31 | 32 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | *.local 30 | *.local.js 31 | 32 | ############## Ignore Test Files ############## 33 | .travis.yml 34 | test 35 | doc 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.4.1" 4 | -------------------------------------------------------------------------------- /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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Javascript Binary Bundles

2 | 3 | [![JBB Version](https://img.shields.io/npm/v/jbb.svg?label=version&maxAge=2592000)](https://www.npmjs.com/package/jbb) [![Build Status](https://travis-ci.org/wavesoft/jbb.svg?branch=master)](https://travis-ci.org/wavesoft/jbb) [![Join the chat at https://gitter.im/wavesoft/jbb](https://badges.gitter.im/wavesoft/jbb.svg)](https://gitter.im/wavesoft/jbb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | **Why Binary Bundles?** For *faster* loading time due to *fewer* requests and an *optimised* binary format, closely compatible with Javascript internals. It is optimised in balance between size and performance, preferring performance when in doubt. 6 | 7 | [Examples](https://github.com/wavesoft/jbb/wiki#examples) — [Documentation](https://github.com/wavesoft/jbb/wiki) — [Tutorials](https://github.com/wavesoft/jbb/wiki#tutorials) — [Help](https://gitter.im/wavesoft/jbb) 8 | 9 | 10 | 11 | With JBB you can load all of your project's resources in a `node.js` instance and then serialize them in a single file. You can then load this file instead. 12 | 13 | ## Usage - Loading Bundles 14 | 15 | Download the [minified run-time library](https://raw.githubusercontent.com/wavesoft/jbb/master/dist/jbb.min.js) and include it in your project: 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | You can then load your bundles like this: 22 | 23 | ```js 24 | var loader = new JBB.BinaryLoader("path/to/bundles"); 25 | loader.add("bundle_name.jbb"); 26 | loader.load(function(error, database) { 27 | // Handle your data 28 | }); 29 | ``` 30 | 31 | ### Using npm 32 | 33 | JBB is also available on npm. Both compiler and run-time library is available in the same package: 34 | 35 | ``` 36 | npm install --save jbb 37 | ``` 38 | 39 | You can then load your bundles like this: 40 | 41 | ```js 42 | var JBBBinaryLoader = require('jbb/decoder'); 43 | 44 | var loader = new JBBBinaryLoader("path/to/bundles"); 45 | loader.add("bundle_name.jbb"); 46 | loader.load(function(error, database) { 47 | // Handle your data 48 | }); 49 | ``` 50 | 51 | ## Usage - Creating Bundles 52 | 53 | After you have [collected your resources in a source bundle](https://github.com/wavesoft/jbb/wiki/Creating-a-Simple-Source-Bundle-%26-Compiling-it) you can then compile it using the `gulp-jbb` plugin. 54 | 55 | In your `gulpfile.js`: 56 | 57 | ```js 58 | var gulp = require('gulp'); 59 | var jbb = require('gulp-jbb'); 60 | 61 | // Compile jbb task 62 | gulp.task('jbb', function() { 63 | return gulp 64 | .src([ "your_bundle.jbbsrc" ]) 65 | .pipe(jbb({ 66 | profile: [ "profile-1", "profile-2" ] 67 | })) 68 | .pipe(gulp.dest( "build/bundles" )); 69 | }); 70 | ``` 71 | 72 | # License 73 | 74 | ``` 75 | Copyright (C) 2015-2016 Ioannis Charalampidis 76 | 77 | Licensed under the Apache License, Version 2.0 (the "License"); 78 | you may not use this file except in compliance with the License. 79 | You may obtain a copy of the License at 80 | 81 | http://www.apache.org/licenses/LICENSE-2.0 82 | 83 | Unless required by applicable law or agreed to in writing, software 84 | distributed under the License is distributed on an "AS IS" BASIS, 85 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 86 | See the License for the specific language governing permissions and 87 | limitations under the License. 88 | ``` 89 | -------------------------------------------------------------------------------- /bin/jbbc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // Override default console functions 4 | var colors = require("colors"), hideWarn = false; 5 | console.warn = function() { if (hideWarn) return; console.log.apply(console, ["WARN:".yellow].concat(Array.prototype.slice.call(arguments)) ); }; 6 | console.error = function() { console.log.apply(console, ["ERROR:".red].concat(Array.prototype.slice.call(arguments)) ); }; 7 | console.info = function() { console.log.apply(console, ["INFO:".green].concat(Array.prototype.slice.call(arguments)) ); }; 8 | console.info("Initializing compiler"); 9 | 10 | // Import dependencies 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | var getopt = require('node-getopt'); 14 | var BinaryEncoder = require("../encoder"); 15 | var BinaryCompiler = require("../compiler"); 16 | 17 | /////////////////////////////////////////////////////////////////////// 18 | // Helper functions 19 | /////////////////////////////////////////////////////////////////////// 20 | 21 | /** 22 | * Create node-getopt object 23 | */ 24 | var createOptions = function( profile ) { 25 | 26 | // Extract profile additions 27 | var profile_additions = []; 28 | if (profile && profile.getopt) 29 | profile_additions = profile.getopt(); 30 | 31 | // Validate command-line 32 | var opt = getopt.create([ 33 | ['p' , 'profile=NAME' , 'Specify the profile to use for the compiler'], 34 | ['o' , 'out=BUNDLE' , 'Specify the name of the bundle'], 35 | ['O' , 'optimise=LEVEL+' , 'Specify optimisation level'], 36 | ['b' , 'basedir=DIR' , 'Specify bundle base directory'], 37 | ].concat( profile_additions ).concat([ 38 | ['' , 'log=FLAGS' , 'Enable logging (see flags below)'], 39 | ['s' , 'sparse' , 'Create a sparse bundle rather than a solid one'], 40 | ['h' , 'help' , 'Display this help'], 41 | ['v' , 'version' , 'Show version'], 42 | ])) 43 | .setHelp( 44 | "Usage: jbb -p -o [OPTION] \n" + 45 | "Compile one or more bundles to a binary format.\n" + 46 | "\n" + 47 | "[[OPTIONS]]\n" + 48 | "\n" + 49 | "Logging Flags:\n" + 50 | "\n" + 51 | " p Log primitive opcodes\n" + 52 | " a Log array opcodes\n" + 53 | " c Log array chunk opcodes\n" + 54 | " b Log array bulk operations opcodes\n" + 55 | " s Log string opcodes\n" + 56 | " r Log internal cross-reference\n" + 57 | " R Log external cross-reference\n" + 58 | " o Log object opcodes\n" + 59 | " e Log file embedding opcodes\n" + 60 | " o Log object opcodes\n" + 61 | " O Log plain object opcodes\n" + 62 | " d Log protocol debug operations\n" + 63 | " w Log low-level byte writes\n" + 64 | " - Log everything\n" + 65 | "\n" + 66 | "Installation: npm install three-bundles\n" + 67 | "Respository: https://github.com/wavesoft/three-bundles" 68 | ) 69 | .bindHelp() // bind option 'help' to default action 70 | .parseSystem(); // parse command line 71 | 72 | // Return options 73 | return opt; 74 | 75 | } 76 | 77 | /** 78 | * Compile log 79 | */ 80 | var getLogFlags = function( logTags ) { 81 | 82 | // If missing return none 83 | if (!logTags) 84 | return 0; 85 | 86 | // Apply logging flags 87 | var logFlags = 0; 88 | for (var i=0; i 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | var BinaryEncoder = require("./encoder"); 22 | var BinaryDecoder = require("./decoder"); 23 | var BundlesLoader = require("./loader"); 24 | var path = require('path'); 25 | var fs = require('fs'); 26 | 27 | /** 28 | * Wrapper function for the compile function, that first loads the 29 | * bundle specs from the source bundle file specified. 30 | */ 31 | function compileFile( sourceBundle, bundleFile, config, callback ) { 32 | var fname = sourceBundle + "/bundle.json"; 33 | fs.readFile(fname, 'utf8', function (err,data) { 34 | if (err) { 35 | console.error("Unable to load file",fname); 36 | if (callback) callback( err, null ); 37 | return; 38 | } 39 | 40 | // Update path if missing 41 | if (!config['path']) 42 | config['path'] = path.dirname( sourceBundle ); 43 | 44 | // Compile 45 | compile( JSON.parse(data), bundleFile, config, callback ); 46 | 47 | }); 48 | } 49 | 50 | /** 51 | * Compile the specifie bundle data to the specified bundle file. 52 | * Additional information, such as profile or other compile-time parameters 53 | * can be specified through the config object. 54 | */ 55 | function compile( bundleData, bundleFile, config, callback ) { 56 | var baseDir = config['path'] || path.dirname(bundleFile); 57 | var encoder, profileEncoder = [], profileLoader = [], bundleLoader; 58 | 59 | // Compile stages 60 | var openBundle = function( cb ) { 61 | return function() { 62 | 63 | // Create encoder 64 | encoder = new BinaryEncoder( 65 | bundleFile, 66 | { 67 | 'name' : bundleData['name'], 68 | 'base_dir' : baseDir, 69 | 'log' : config['log'] || 0x00, 70 | 'sparse' : config['sparse'], 71 | } 72 | ); 73 | 74 | // Add profiles 75 | for (var i=0; io;++o)s[o]=pivot+a[o]*n;return s}function o(t,r,e,i){var s=I.FROM[i],n=new m[s](e),a=0;switch(s){case 0:a=t.u8[t.i8++];break;case 1:a=t.s8[t.i8++];break;case 2:a=t.u16[t.i16++];break;case 3:a=t.s16[t.i16++];break;case 4:a=t.u32[t.i32++];break;case 5:a=t.s32[t.i32++];break;case 6:a=t.f32[t.i32++];break;case 7:a=t.f64[t.i64++]}var o=t.readTypedArray(I.TO[i],e-1);n[0]=a;for(var h=0,u=o.length;u>h;++h)a+=o[h],n[h+1]=a;return n}function h(t,r){var e=t.u16[t.i16++],i=t.signature_table[e],s=t.factory_plain_bulk[e];if(void 0===s)throw new w.AssertError("Could not found simple object signature with id #"+e+"!");for(var n=l(t,r),a=n.length/i.length,o=new Array(a),h=0;a>h;++h)o[h]=s(n,h);return o}function u(t,r,e){var i=t.u16[t.i16++],s=t.profile.decode(i),n=s.props,a=0,o=[],h=0,u=0,f=0,c=null,p=0,d=0,v=[],b=[];for(f=65536>e?t.u16[t.i16++]:t.u32[t.i32++],o=Array(f),h=0;f>h;++h)t.iref_table.push(o[h]=s.create());if(f){if(v=l(t,r),void 0===v.length)throw new w.AssertError("Decoded known bulk primitive is not array!");a=v.length/n}var y=new Array(e),g=0,T=void 0;for(h=0;e>h;)if(d=t.u8[t.i8++],(128&d)===k.LREF_7)u=127&d,y[h++]=T=b[u];else if((192&d)===k.DEFINE){for(u=(63&d)+1,p=0;u>p;p++)c=o[g],s.init(c,v,a,g),y[h++]=c,g++;T=c}else if((224&d)===k.REPEAT)for(u=(31&d)+1,p=0;u>p;p++)y[h++]=T;else if((240&d)===k.IREF){if(u=(15&d)<<16|t.u16[t.i16++],u>=t.iref_table.length)throw new w.IRefError("Invalid IREF #"+u+"!");y[h++]=T=t.iref_table[u],b.push(t.iref_table[u])}else if((248&d)===k.LREF_11)u=(7&d)<<8|t.u8[t.i8++],y[h++]=T=b[u];else if(d===k.LREF_16)u=t.u16[t.i16++],y[h++]=T=b[u];else if(d===k.XREF){if(u=t.readStringLT(),void 0===r[u])throw new w.XRefError("Cannot import undefined external reference "+u+"!");y[h++]=T=r[u]}return y}function f(t,r,e,i){for(var s,n,a=t.u8[t.i8++],o=[],h=0,u=!0,f=0,l=0,p=0,d=0,v=0,b=0;e>h;){if(i)if(64===(240&a)){switch(p=1&a,d=a>>1&7,v=p?t.u32[t.i32++]:t.u16[t.i16++],u&&(n=new m[d](e),u=!1),d){case 0:b=t.u8[t.i8++];break;case 1:b=t.s8[t.i8++];break;case 2:b=t.u16[t.i16++];break;case 3:b=t.s16[t.i16++];break;case 4:b=t.u32[t.i32++];break;case 5:b=t.s32[t.i32++];break;case 6:b=t.f32[t.i32++];break;case 7:b=t.f64[t.i64++]}n.fill(b,h,h+v),h+=v}else{if(s=c(t,r,a),void 0===s.length)throw new w.AssertError("Encountered non-array chunk as part of chunked array!");if(u&&(n=new s.constructor(e),u=!1),!(s instanceof n.constructor))throw new w.AssertError("Got is_numeric flag, but chunks are not of the same type (op was "+a+"!");n.set(s,h),h+=s.length}else{if(s=c(t,r,a),void 0===s.length)throw new w.AssertError("Encountered non-array chunk as part of chunked array!");for(u&&(o=new Array(e),u=!1),f=0,l=s.length;l>f;f++,h++)o[h]=s[f]}if(!(e>h))break;a=t.u8[t.i8++]}return i?n:o}function c(t,r,e){var i,s=0,n=0,c=0,p=0,d=[];if(e=255&e,110===e)return h(t,r);if(111===e){for(p=t.u8[t.i8++],d[p-1]=null,s=0;p>s;++s)d[s]=l(t,r);return d}if(126===e)return[];if(127===e)throw new w.AssertError("Encountered PRIM_CHUNK_END outside of chunked array!");if(0===(224&e)){c=1&e,n=e>>1&15,p=c?t.u32[t.i32++]:t.u16[t.i16++],i=t.readTypedArray(E.TO[n],p);var v=new m[E.FROM[n]](i);return v}if(32===(240&e))return c=1&e,n=e>>1&7,p=c?t.u32[t.i32++]:t.u16[t.i16++],o(t,r,p,n);if(48===(240&e))return c=1&e,n=e>>1&7,p=c?t.u32[t.i32++]:t.u16[t.i16++],a(t,r,p,n);if(64===(240&e)){switch(c=1&e,n=e>>1&7,p=c?t.u32[t.i32++]:t.u16[t.i16++],n){case 0:i=t.u8[t.i8++];break;case 1:i=t.s8[t.i8++];break;case 2:i=t.u16[t.i16++];break;case 3:i=t.s16[t.i16++];break;case 4:i=t.u32[t.i32++];break;case 5:i=t.s32[t.i32++];break;case 6:i=t.f32[t.i32++];break;case 7:i=t.f64[t.i64++]}var v=new m[n](p);for(s=0;p>s;++s)v[s]=i;return v}if(80===(240&e))return c=1&e,n=e>>1&7,p=c?t.u32[t.i32++]:t.u16[t.i16++],d=t.readTypedArray(n,p);if(96===(248&e))return n=7&e,p=t.u8[t.i8++],d=t.readTypedArray(n,p);if(104===(254&e)){for(c=1&e,p=c?t.u32[t.i32++]:t.u16[t.i16++],i=l(t,r),d[p-1]=null,s=0;p>s;s++)d[s]=i;return d}if(106===(254&e)){for(c=1&e,p=c?t.u32[t.i32++]:t.u16[t.i16++],d[p-1]=null,s=0;p>s;++s)d[s]=l(t,r);return d}if(120===(252&e))return c=1&e,n=e>>1&1,p=c?t.u32[t.i32++]:t.u16[t.i16++],f(t,r,p,n);if(124===(254&e))return c=1&e,p=c?t.u32[t.i32++]:t.u16[t.i16++],u(t,r,p);throw new w.AssertError("Unknown array op-code 0x"+e.toString(16)+" at offset @"+(t.i8-t.ofs8)+"!")}function l(t,r){var e=t.u8[t.i8++];if(0===(128&e))return c(t,r,127&e);if(128===(192&e))return n(t,r,63&e);if(192===(224&e))return s(t,(24&e)>>3,7&e);if(224===(240&e)){var i=t.u16[t.i16++];if(i=(15&e)<<16|i,i>=t.iref_table.length)throw new w.IRefError("Invalid IREF #"+i+"!");return t.iref_table[i]}if(240===(248&e))switch(7&e){case 0:return t.u8[t.i8++];case 1:return t.s8[t.i8++];case 2:return t.u16[t.i16++];case 3:return t.s16[t.i16++];case 4:return t.u32[t.i32++];case 5:return t.s32[t.i32++];case 6:return t.f32[t.i32++];case 7:return t.f64[t.i64++]}else{if(248===(252&e))return U[3&e];if(252===(254&e))return N[2&e];if(254===(255&e)){var a=t.readStringLT();if(void 0===r[a])throw new w.XRefError("Cannot import undefined external reference "+a+"!");return r[a]}if(255===(255&e))throw new w.AssertError("Encountered RESERVED primitive operator!")}}function p(t,r){for(;!t.eof();){var e=t.u8[t.i8++];switch(e){case 248:var i=t.prefix+t.readStringLT();r[i]=l(t,r);break;default:throw new w.AssertError("Unknown control operator 0x"+e.toString(16)+" at @"+t.i8+"!")}}}function d(t,r,e){var i=t.length,s=Array(i),n=!1;return t.forEach(function(a,o){var h=new XMLHttpRequest;h.open("GET",t[o]),h.responseType="arraybuffer",h.send(),h.addEventListener("progress",function(t){t.lengthComputable&&r.update(h,t.loaded,t.total)}),h.addEventListener("readystatechange",function(){if(4===h.readyState)if(200===h.status)s[o]=h.response,0===--i&&(r.complete(),e(null));else{if(n)return;n=!0,r.complete(),e("Error loading "+t[o]+": "+h.statusText)}})}),s}var v=e(1),b=e(2),y=e(3),w=e(4);const g=0,T=1,A={UINT8:0,INT8:1,UINT16:2,INT16:3,UINT32:4,INT32:5,FLOAT32:6,FLOAT64:7},E={FROM:[A.UINT16,A.INT16,A.UINT32,A.INT32,A.UINT32,A.INT32,A.FLOAT32,A.FLOAT32,A.FLOAT32,A.FLOAT32,A.FLOAT64,A.FLOAT64,A.FLOAT64,A.FLOAT64,A.FLOAT64],TO:[A.UINT8,A.INT8,A.UINT8,A.INT8,A.UINT16,A.INT16,A.UINT8,A.INT8,A.UINT16,A.INT16,A.UINT8,A.INT8,A.UINT16,A.INT16,A.FLOAT32]},I={FROM:[A.UINT16,A.INT16,A.UINT32,A.INT32,A.UINT32,A.INT32],TO:[A.INT8,A.INT8,A.INT8,A.INT8,A.INT16,A.INT16]},_={FROM:[A.FLOAT32,A.FLOAT32,A.FLOAT64,A.FLOAT64],TO:[A.INT8,A.INT16,A.INT8,A.INT16]},m=[Uint8Array,Int8Array,Uint16Array,Int16Array,Uint32Array,Int32Array,Float32Array,Float64Array],k=([A.UINT16,A.UINT32],[A.UINT8,A.UINT16,A.UINT32,A.FLOAT64],{LREF_7:0,LREF_11:240,LREF_16:254,IREF:224,XREF:255,DEFINE:128,REPEAT:192}),U=[void 0,null,!1,!0],N=[NaN];var F=function(t,r){"object"==typeof t&&(r=t,t=""),this.database=r||{},this.baseDir=t||"",this.queuedRequests=[],this.profile=new b,this.progressManager=new y};F.prototype={constructor:F,addProfile:function(t){this.profile.add(t)},add:function(t,r){var e,i="";this.baseDir&&(i=this.baseDir+"/");var s=t.split("?"),n="",a=[];if(s.length>1&&(n="?"+s[1]),".jbbp"===s[0].substr(s[0].length-5).toLowerCase()){var e=i+s[0].substr(0,s[0].length-5);a=[e+".jbbp"+n,e+"_b16.jbbp"+n,e+"_b32.jbbp"+n,e+"_b64.jbbp"+n]}else{var t=s[0];".jbb"!=t.substr(t.length-4)&&(t+=".jbb"),a=[i+t+n]}var o={callback:r,status:g,buffer:void 0,url:a};this.queuedRequests.push(o)},addByBuffer:function(t,r){var e={callback:r,status:T,buffer:t,url:void 0};this.queuedRequests.push(e)},addProgressHandler:function(t){this.progressManager.addHandler(t)},removeProgressHandler:function(t){this.progressManager.removeHandler(t)},load:function(t){this.__process(t)},__process:function(t){var r=this;if(t||(t=function(){}),0===this.queuedRequests.length)return void t(null,this.database);for(var e=!1,i=0;ie;++e)0!==e&&(r+=","),r+="p[i+c*"+e+"]";return new Function("p","i","var c=p.length/"+t+";return ["+r+"]")}var i=function(t,r){this.profile=r,this.prefix="",this.sparse=t instanceof Array;var i,s,n,a=32;if(this.sparse?(this.u8=new Uint8Array(t[0]),this.s8=new Int8Array(t[0]),this.u16=new Uint16Array(t[1]),this.s16=new Int16Array(t[1]),this.u32=new Uint32Array(t[2]),this.s32=new Int32Array(t[2]),this.f32=new Float32Array(t[2]),this.f64=new Float64Array(t[3]),i=new Uint16Array(t[0],0,a/2),s=new Uint32Array(t[0],0,a/4),n=t[0].byteLength):(this.u8=new Uint8Array(t),this.s8=new Int8Array(t),this.u16=new Uint16Array(t),this.s16=new Int16Array(t),this.u32=new Uint32Array(t),this.s32=new Int32Array(t),this.f32=new Float32Array(t),this.f64=new Float64Array(t),i=new Uint16Array(t),s=new Uint32Array(t),n=t.byteLength),this.magic=i[0],this.table_id=i[1],this.version=i[2],this.table_count=i[3],this.ver_major=255&this.version,this.ver_minor=(65280&this.version)>>8,this.max64=s[2],this.max32=s[3],this.max16=s[4],this.max8=s[5],this.lenST=s[6],this.lenOT=s[7],12610==this.magic)throw{name:"EndianessError",message:"Unfortunately the JBB format is currently only compatible with Little-Endian CPUs",toString:function(){return this.name+": "+this.message}};if(16945!=this.magic)throw{name:"DecodingError",message:"This does not look like a JBB archive! (Magic is 0x"+this.magic.toString(16)+")",toString:function(){return this.name+": "+this.message}};if(258!=this.version)throw{name:"DecodingError",message:"Unsupported bundle version v"+this.ver_minor+"."+this.ver_minor,toString:function(){return this.name+": "+this.message}};this.sparse?(this.i64=0,this.i32=0,this.i16=0,this.i8=a,this.iEnd=this.i8+this.max8-this.lenST,this.ofs8=this.i8,this.ofs16=this.i16,this.ofs32=this.i32,this.ofs64=this.i64):(this.i64=a,this.i32=this.i64+this.max64,this.i16=this.i32+this.max32,this.i8=this.i16+this.max16,this.iEnd=this.i8+this.max8-this.lenST,this.ofs8=this.i8,this.ofs16=this.i16,this.ofs32=this.i32,this.ofs64=this.i64,this.i16/=2,this.i32/=4,this.i64/=8);var o=[];if(this.table_count<=1)0!==this.table_id&&o.push(this.table_id);else{if(0!==this.table_id)throw{name:"DecodingError",message:"Invalid header data",toString:function(){return this.name+": "+this.message}};for(var h=0;h0){for(var c=this.i8+this.max8,h=c-this.lenST;c>h;++h){var l=this.u8[h];0===l?(this.string_table.push(u),u="",f=!1):(u+=String.fromCharCode(l),f=!0)}f&&this.string_table.push(u)}var p=[],d=0,v=!1;if(this.signature_table=[],this.lenOT>0){for(var c=2*this.i16+this.max16,h=c-this.lenOT;c>h;h+=2){var l=this.u16[h/2];d--?p.push(this.string_table[l]):(v&&this.signature_table.push(p),p=[],d=l,v=!0)}v&&this.signature_table.push(p)}this.sparse?this.readTypedArray=function(r,e){var i=this.i8,s=2*this.i16,n=4*this.i32,a=8*this.i64;switch(r){case 0:return this.i8+=e,new Uint8Array(t[0],i,e);case 1:return this.i8+=e,new Int8Array(t[0],i,e);case 2:return this.i16+=e,new Uint16Array(t[1],s,e);case 3:return this.i16+=e,new Int16Array(t[1],s,e);case 4:return this.i32+=e,new Uint32Array(t[2],n,e);case 5:return this.i32+=e,new Int32Array(t[2],n,e);case 6:return this.i32+=e,new Float32Array(t[2],n,e);case 7:return this.i64+=e,new Float64Array(t[3],a,e)}}:this.readTypedArray=function(r,e){var i=this.i8,s=2*this.i16,n=4*this.i32,a=8*this.i64;switch(r){case 0:return this.i8+=e,new Uint8Array(t,i,e);case 1:return this.i8+=e,new Int8Array(t,i,e);case 2:return this.i16+=e,new Uint16Array(t,s,e);case 3:return this.i16+=e,new Int16Array(t,s,e);case 4:return this.i32+=e,new Uint32Array(t,n,e);case 5:return this.i32+=e,new Int32Array(t,n,e);case 6:return this.i32+=e,new Float32Array(t,n,e);case 7:return this.i64+=e,new Float64Array(t,a,e)}},this.factory_plain=[],this.factory_plain_bulk=[];for(var h=0;hT;++T)w+="'"+b[T]+"': values["+T+"],",g+="'"+b[T]+"': values[i+c*"+T+"],";w+="}",g+="}",this.factory_plain.push(new Function("values",w)),this.factory_plain_bulk.push(new Function("values","i",g))}this.factory_weave=[function(t,r){return[]}];for(var h=1;16>h;++h)this.factory_weave.push(e(h))};i.prototype.readStringLT=function(){var t=this.u16[this.i16++];if(t>=this.string_table.length)throw{name:"RangeError",message:"String ID is outside than the range of the string lookup table!",toString:function(){return this.name+": "+this.message}};return this.string_table[t]},i.prototype.eof=function(){return this.i8>=this.iEnd},i.prototype.where=function(){console.log("i8=",this.i8-this.ofs8," [U:",this.u8[this.i8],"0x"+this.u8[this.i8].toString(16),"/ S:",this.s8[this.i8],"]"),console.log("i16=",this.i16-this.ofs16/2," [U:",this.u16[this.i16],"/ S:",this.s16[this.i16],"]"),console.log("i32=",this.i32-this.ofs32/4," [U:",this.u32[this.i32],"/ S:",this.s32[this.i32],"/ F:",this.f32[this.i32],"]"),console.log("i64=",this.i64-this.ofs64/8," [F:",this.f64[this.i64],"]")},t.exports=i},function(t,r){"use strict";var e=function(){this.size=0,this._lib=[],this._profiles=[],this._offset=0,this._foffset=0};e.prototype.add=function(t){this._lib.push(t)},e.prototype.use=function(t){if(0!=t.length)for(var r=0,e=t.length;e>r;r++){for(var i=null,s=t[r],n=0,a=this._lib.length;a>n;n++){var o=this._lib[n];if(o.id===s){i=o;break}}if(null===i){var h=(65520&s)>>4,u=15&s;throw{name:"DecodingError",message:"The bundle uses profile 0x"+h.toString(16)+", rev "+u+" but it was not loaded!",toString:function(){return this.name+": "+this.message}}}this.size+=i.size,this._profiles.push([i,this._foffset,this._foffset+i.frequent,this._offset,this._offset+i.size-i.frequent]),this._offset+=i.size-i.frequent,this._foffset+=i.frequent}},e.prototype.decode=function(t){if(32>t)for(var r=0,e=this._profiles.length;e>r;++r){var i=this._profiles[r],s=i[0],n=i[1],a=i[2];if(t>=n&&a>t)return s.decode(t-n)}else{t-=32;for(var r=0,e=this._profiles.length;e>r;++r){var i=this._profiles[r],s=i[0],n=i[3],a=i[4];if(t>=n&&a>t)return s.decode(t-n+32)}}},t.exports=e},function(t,r){"use strict";var e=function(t){this.parent=t,this.objectLookup=[],this.objects=[],this.value=0};e.prototype.update=function(t,r,e){var i=this.objectLookup.indexOf(t);-1===i?(i=this.objectLookup.length,this.objectLookup.push(t),this.objects.push([r,e])):(this.objects[i][0]=r,this.objects[i][1]=e);for(var s=0,n=0,a=0;a 10 | 11 | Offset 12 | Region 13 | 14 | 15 | 0x0000 16 | Header (see below) 17 | 18 | 19 | 0x0018 20 | 64-Bit Elements (Float64) 21 | 22 | 23 | ... 24 | 32-Bit Elements (Float32, Int32, UInt32) 25 | 26 | 27 | ... 28 | 16-Bit Elements (Int16, UInt16) 29 | 30 | 31 | ... 32 | Object Signatures 33 | 34 | 35 | ... 36 | 8-Bit Elements (Int8, UInt8) 37 | 38 | 39 | ... 40 | Strings (NULL Terminated) 41 | 42 | 43 | 44 | The entry point of the bundle is the beginning of the 8-bit elements, since all the op-codes are 8-bit long (`UInt8`). 45 | 46 | *NOTE:* When using sparse format, the four streams are separated in four different files that can be loaded in parallel. 47 | 48 | ## Header 49 | 50 | _Revision Notice: The previous version of the protocol was using the 0x4233 magic number in the header._ 51 | 52 | The file header is organised as shown below. The `Magic` number is a 16-bit unsigned integer with value `0x4231`, so it's represented as `<31h,42h>` in machines with little-endian architecture or `<42h,31h>` in big-endian. It's therefore possible to detect incompatible endianess before loading the bundle. 53 | 54 | The `Object Table ID` is the ID of the Object Table used to compile this bundle. This table contains the information required to re-construct the objects in the bundle and should be provided by the loader arguments. 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 |
Offset+1+2+3+4
0x00Magic (0x4231)Object Table ID
0x04Protocol Version (1)Reserved
0x0864-Bit Table Size (Bytes)
0x0C32-Bit Table Size (Bytes)
0x1016-Bit Table Size (Bytes)
0x148-Bit Table Size (Bytes)
0x18String Table Size (Bytes)
0x1CObject Signature Table Size (Bytes)
99 | 100 | ## Primitives 101 | 102 | The binary bundle is a compacted representation of javascript objects, with size and speed optimisations. In addition, it offers a symbol import/export functionality for sharing resources among different bundles. 103 | 104 | The data in a bundle file are organised in _Primitives_, each one being one of the following: 105 | 106 | * __SIMPLE__ - A simple primitive, that being: 107 | - `UNDEFINED` - The value `undefined` 108 | - `NULL` - The value `null` 109 | - `FALSE` - The boolean value `false` 110 | - `TRUE` - The boolean value `true` 111 | - `NAN` - The value `NaN` 112 | * __ARRAY__ - Array of primitives, further specialised as: 113 | - `EMPTY` - Empty Array 114 | - `DELTA` - Delta-Encoded TypedArray 115 | - `REPEATED` - Repeated TypedArray 116 | - `DOWNSCALED` - Downscaled TypedArray 117 | - `SHORT` - Short TypedArray (1-256 numbers) 118 | - `PRIMITIVE` - Primitive Array (further optimised in chunks) 119 | - `RAW` - RAW Typed Array 120 | * __OBJECT__ - A Javascript object, further specialised as: 121 | - `PREDEFINED` - A predefined object from the object table 122 | - `PLAIN` - A plain object 123 | - `PRIMITIVE` - A primitive javascript object, such as `Date` 124 | * __BUFFER__ - Buffered contents, further specialised as: 125 | - `STRING_LATIN` - A string in LATIN-1 (ISO-8859-1) encoding 126 | - `STRING_UTF8` - A string un UTF-8 encoding 127 | - `BUF_IMAGE` - An embedded image (ImageDOMElement) 128 | - `BUF_SCRIPT` - An embedded script (ScriptDOMElement) 129 | - `RESOURCE` - An arbitrary payload embedded as resource 130 | * __NUMBER__ - A single number 131 | * __IMPORT__ - Import a primitive from another bundle 132 | * __REF__ - Reference to a primitive in the same bundle 133 | 134 | The object primitives are composites. Internally the use array primitives to represent the series of property names and the property values. Therefore the object properties can be further optimised by the array optimisation functions. 135 | 136 | ## Optimisations 137 | 138 | There are a series of optimisations used in this file format. The goal is to have the smallest file size possible that will not impact the parsing speed. This means avoiding `for` loops, using TypedArrays when possible and de-duplicating entries in the file. In addition, to further reduce the file size, the type of the TypedArray is chosen dynamically by analysing the values of the array. 139 | 140 | ### Table of Known Objects 141 | 142 | Most of the times, some objects can be represented with less properties than appear in their instance. In addition, to further reduce the amount of information stored in the file, this information is stored in a separate table, called `Object Table`. 143 | 144 | This table is specific to the application and therefore maintained separately from the `jbb` project. For example, have a look on the [jbb-profile-three](https://github.com/wavesoft/jbb-profile-three) profile. 145 | 146 | ### De-duplication 147 | 148 | De-duplication occurs only for the `OBJECT` primitives, by internally referencing same or similar object in the bundle. The detection of the duplicate items is achieved either by reference or by value: 149 | 150 | * __By Reference__ is enabled by default and looks for exact matches of the object in the object table. 151 | * __By Value__ is enabled by the user (because it slows down the compilation time), and looks for exact match of the values of the object in the object table. 152 | 153 | ### Compression 154 | 155 | In addition to de-duplication, there are some cases that we can benefit from numerical type downscaling, with a minor performance loss. Therefore compression occurs only on the `ARRAY` primitives: 156 | 157 | * __Delta Encoding__ is used when the values in the array have a small difference (delta) between them. In this case, they are encoded as an array of smaller type size. For instance `Int32Array` can be encoded as `Int16Array` or even `Int8Array` if the differences are small. This can also be used with `Float32Array` in conjunction with scaled integers (ex. ±`Int16`*0.001). 158 | * __Downscaling__ is used when the values of an array are able to fit on an array with smaller type. Therefore an `Int32Array` can be downscaled to `Int16Array` or event `Int8Array`. The original array type is preserved and restored at decoding. 159 | * __Repeated__ is used when the values of the array is simply a single value repeated. 160 | * __Chunked Primitive__ is used when an array mixes primitives and numerical values. The encoding algorithm will try to split the array in chunks if similar types that can be further optimised with one of the above techniques. 161 | 162 | ## Opcodes 163 | 164 | The following table explains the binary opcodes found in the bundle format: 165 | 166 | ### Control Opcodes 167 | The control opcodes prefix primitives or other control/information data in the file. 168 | 169 | 170 | 171 | ### Primitive Opcodes 172 | The primitive opcodes define the type of the primitive being encoded: 173 | 174 | 175 | 176 | ### Object Opcodes 177 | This is a specialisation of the OBJECT primitive. 178 | 179 | 180 | 181 | ### Array Opcodes 182 | This is a specialisation of the ARRAY primitive. 183 | 184 | 185 | 186 | ### PRIM_BULK_KNOWN Steering OP-Codes 187 | This is a set of steering instructions for the `PRIM_BULK_KNOWN` array. 188 | 189 | 190 | 191 | ### Other Types 192 | The following tables contain the values of other properties of the opcodes. 193 | 194 | 195 | 196 | 199 | 202 | 205 | 206 | 207 | 210 | 213 | 216 | 219 | 220 | 221 | 224 | 227 | 228 | 229 |
197 | 198 | 200 | 201 | 203 | 204 |
208 | 209 | 211 | 212 | 214 | 215 | 217 | 218 |
222 | 223 | 225 | 226 |
230 | -------------------------------------------------------------------------------- /doc/Tutorial 1 - THREEjs/Using with THREEjs.md: -------------------------------------------------------------------------------- 1 | 2 | # Using `jbb` with `THREE.js` 3 | 4 | The JBB Project was initially started in order to optimally store resources for THREE.js scenes. Therefore it's only natural to have a dedicated guide on how to use it with three.js. 5 | 6 | You can integrate jbb in your pipeline in various ways. In this tutorial we are going to use `gulp-jbb` for steering the creation of bundles, but we are not going to create a gulp-based binary project. 7 | 8 | ## 1. Getting Started 9 | 10 | For the bare minimum requirements you will need to install `node` and a few packages. Start by creating a new directory for our project and install the following packages locally: 11 | 12 | ``` 13 | npm install jbb jbb-profile-three gulp gulp-jbb three 14 | ``` 15 | 16 | Then create two directories: 17 | * `bundles` - Were the bundle sources are located. 18 | * `build` - Were the built files will be placed. 19 | 20 | ``` 21 | mkdir bundles build 22 | ``` 23 | 24 | ## 3. Creating a THREE.js scene 25 | 26 | Let's start by creating a new page for our three.js scene. Create a new file `index.html` in the root directory with the following contents: 27 | 28 | ```html 29 | 30 | 31 | 32 | three.js + jbb.js 33 | 34 | 35 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 109 | 110 | 111 | ``` 112 | 113 | Open your `index.html` file and check your javascript console. If you see no errors you are ready to continue. Don't worry about the empty black content for now... 114 | 115 | ## 2. Creating a bundle 116 | 117 | For our example we are going to bundle a textured cube. We used [Blender](https://www.blender.org/) and [THREE.js exporter](https://github.com/mrdoob/three.js/tree/master/utils/exporters/blender) to create a very simple crate: 118 | 119 | 120 | 121 | After exporting the cube, we have two files: `crate.json` and `crate.png`. The first contains the cube mesh and the second the diffuse map for the texture. You can find the files [here](https://github.com/wavesoft/jbb/tree/master/doc/Tutorial 1 - THREEjs/files). 122 | 123 | To create a new bundle, we are going to create a new bundle directory in the `bundles` folder and a new specifications file. 124 | 125 | ``` 126 | mkdir bundles/crate.jbbsrc 127 | ``` 128 | 129 | Move the two files in the `crate.jbbsrc` directory and create an additional new file called `bundle.json` with the following contents: 130 | 131 | ```json 132 | { 133 | "name": "crate", 134 | "exports": { 135 | "THREE.JSONLoader": { 136 | "crate" : "crate.json", 137 | } 138 | } 139 | } 140 | ``` 141 | 142 | This is the _index_ file of the bundle and instructs jbb to first load `crate.json` using the `THREE.JSONLoader` and to export it under the name `crate/crate`. The first, `name` argument should be the same as the bundle folder name (without the extension). 143 | 144 | ## 3. Creating a Gulpfile.js 145 | 146 | In order to build our bundles we can either use the `jbb` command-line, or we can create a gulpfile to automate the build process using `gulp`. 147 | 148 | Create a new file called `gulpfile.js` in your root directory with the following contents: 149 | 150 | ```javascript 151 | var gulp = require('gulp'); 152 | var jbb = require('gulp-jbb'); 153 | 154 | // 155 | // Compile the binary bundles 156 | // 157 | gulp.task('bundles', function() { 158 | return gulp 159 | .src([ "bundles/*.jbbsrc" ]) 160 | .pipe(jbb({ 161 | profile: 'three' 162 | })) 163 | .pipe(gulp.dest('build/bundles')); 164 | }); 165 | 166 | // By default build bundles 167 | gulp.task('default', ['bundles' ]); 168 | ``` 169 | 170 | Note the `profile` parameter in the `jbb` group. It instructs jbb to use the [jbb-profile-three](github.com/wavesoft/jbb-profile-three) in order to correctly handle THREE.js objects. Without it `jbb` will complain about unknown object instances. 171 | 172 | You can now build your bundle just by running `gulp` 173 | 174 | ``` 175 | $ gulp 176 | [21:07:10] Using gulpfile .. /gulpfile.js 177 | [21:07:10] Starting 'bundles'... 178 | Loader.createMaterial: Unsupported colorAmbient [ 0.64, 0.64, 0.64 ] 179 | Loading .. /bundles/crate.jbbsrc/crate.png 180 | [21:07:10] Finished 'bundles' after 181 | [21:07:10] Starting 'default'... 182 | [21:07:10] Finished 'default' after 115 μs 183 | ``` 184 | 185 | You can confirm that `crate.jbb` exists in your `build/bundles` directory. 186 | 187 | ## 4. Loading the bundle in the scene 188 | 189 | Now that we have our bundle, it's time to load it into our THREE.js scene. To do so, we are going to use the `JBBBundleLoader` that was made available when we included the `jbb.min.js` file. 190 | 191 | Change the last part of the init() function like this: 192 | 193 | ```javascript 194 | // TODO : Add something on the scene 195 | 196 | var bundleLoader = new JBBBinaryLoader(); 197 | bundleLoader.addProfile( JBBProfileThree ); 198 | 199 | // Get model from bundle 200 | bundleLoader.add("build/bundles/crate.jbb"); 201 | bundleLoader.load( function(error, database) { 202 | 203 | var geometry = database['crate/crate']; 204 | var materials = database['crate/crate:extra']; 205 | 206 | var mesh = new THREE.Mesh( geometry, materials[0] ); 207 | scene.add(mesh); 208 | 209 | }); 210 | ``` 211 | 212 | Let's see this step-by-step: First, we are creating a new instance to the `JBBBinaryLoader` class. This is responsible for loading and parsing the bundles. 213 | 214 | ```javascript 215 | var bundleLoader = new JBBBinaryLoader(); 216 | ``` 217 | 218 | Right after, we are adding a decoding profile to the loader using the `.addProfile()` method. Similar to the encoding process, a profile contains the decoding information for the objects. 219 | 220 | We have already loaded `jbb-profile-three.min.js`, that exposes the `JBBProfileThree` global variable, so we can use it right-away: 221 | 222 | ```javascript 223 | bundleLoader.addProfile( JBBProfileThree ); 224 | ``` 225 | 226 | Then, we request jbb loader to load our crate bundle. We can add more bundles if needed. Additional bundles might also be loaded if a dependency is encountered. 227 | 228 | ```javascript 229 | bundleLoader.add("build/bundles/crate.jbb"); 230 | ``` 231 | 232 | To actually start loading the bundle we are calling the `.load` function. This will trigger a callback when the operation has completed. 233 | 234 | ```javascript 235 | bundleLoader.load( function(error, database) { 236 | ... 237 | }); 238 | ``` 239 | 240 | If there was no error while loading the bundle, the `error` argument will be `null` and the `database` argument will be a dictionary-like object with all the loaded objects. 241 | 242 | Therefore, we can easily access the encoded crate like this: 243 | 244 | ```javascript 245 | var geometry = database['crate/crate']; 246 | ``` 247 | 248 | Since the cube stored in `crate.json` file is not an object, but just the geometry to the cube, the `THREE.JSONLoader` will create a `THREE.Geometry` object. Since that's the loader's default return value, this geometry will be accessible as `/`. 249 | 250 | However `THREE.JSONLoader` also returns an array of `THREE.Material` instances as a separate argument. Such 'extra' information is available under the `/:extra` name. 251 | 252 | Therefore we have enough information to create a new textured mesh: 253 | 254 | ```javascript 255 | var materials = database['crate/crate:extra']; 256 | 257 | var mesh = new THREE.Mesh( geometry, materials[0] ); 258 | scene.add(mesh); 259 | ``` 260 | 261 | If everything went as expected, you will see something like this: 262 | 263 | 264 | 265 | If you are using `Chrome` you will need to see this page over `http:`. You can use the minimalistic http server from node to serve your resources locally: 266 | 267 | ``` 268 | ~$ npm install http-server 269 | ~$ ./node_modules/http-server/bin/http-server 270 | Starting up http-server, serving ./ 271 | Available on: 272 | http://127.0.0.1:8080 273 | ``` 274 | 275 | And you can now see your results on [http://127.0.0.1:8080](http://127.0.0.1:8080). 276 | 277 | -------------------------------------------------------------------------------- /doc/Tutorial 1 - THREEjs/files/crate.json: -------------------------------------------------------------------------------- 1 | { 2 | "normals": [0.577349,-0.577349,-0.577349,0.577349,-0.577349,0.577349,-0.577349,-0.577349,0.577349,-0.577349,-0.577349,-0.577349,0.577349,0.577349,-0.577349,-0.577349,0.577349,-0.577349,-0.577349,0.577349,0.577349,0.577349,0.577349,0.577349], 3 | "uvs": [[0,0,1,0,1,1,0,1]], 4 | "name": "CubeGeometry.1", 5 | "materials": [{ 6 | "colorDiffuse": [0.64,0.64,0.64], 7 | "DbgIndex": 0, 8 | "colorEmissive": [0,0,0], 9 | "wireframe": false, 10 | "mapDiffuseWrap": ["RepeatWrapping","RepeatWrapping"], 11 | "colorAmbient": [0.64,0.64,0.64], 12 | "specularCoef": 50, 13 | "transparent": false, 14 | "colorSpecular": [0,0,0], 15 | "mapDiffuseAnisotropy": 1, 16 | "opacity": 1, 17 | "blending": "NormalBlending", 18 | "visible": true, 19 | "depthWrite": true, 20 | "DbgName": "Material", 21 | "depthTest": true, 22 | "DbgColor": 15658734, 23 | "mapDiffuse": "crate.png", 24 | "mapDiffuseRepeat": [1,1], 25 | "shading": "phong" 26 | }], 27 | "vertices": [1,-1,-1,1,-1,1,-1,-1,1,-1,-1,-1,1,1,-1,0.999999,1,1,-1,1,1,-1,1,-1], 28 | "faces": [43,0,1,2,3,0,0,1,2,3,0,1,2,3,43,4,7,6,5,0,0,1,2,3,4,5,6,7,43,0,4,5,1,0,0,1,2,3,0,4,7,1,43,1,5,6,2,0,0,1,2,3,1,7,6,2,43,2,6,7,3,0,0,1,2,3,2,6,5,3,43,4,0,3,7,0,0,1,2,3,4,0,3,5], 29 | "metadata": { 30 | "normals": 8, 31 | "uvs": 1, 32 | "materials": 1, 33 | "faces": 6, 34 | "generator": "io_three", 35 | "type": "Geometry", 36 | "vertices": 8, 37 | "version": 3 38 | } 39 | } -------------------------------------------------------------------------------- /doc/Tutorial 1 - THREEjs/files/crate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/Tutorial 1 - THREEjs/files/crate.png -------------------------------------------------------------------------------- /doc/Tutorial 1 - THREEjs/img/1-blender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/Tutorial 1 - THREEjs/img/1-blender.png -------------------------------------------------------------------------------- /doc/Tutorial 1 - THREEjs/img/2-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/Tutorial 1 - THREEjs/img/2-result.png -------------------------------------------------------------------------------- /doc/bundle.json.md: -------------------------------------------------------------------------------- 1 | # bundle.json 2 | 3 | The following example describes the full parameter set that you can use 4 | in your own `bundle.json` file: 5 | 6 | ```javascript 7 | { 8 | // 9 | // -= name =- 10 | // 11 | // The name of the bundle, used internally or tagging the contents of 12 | // the bundle. This name SHOULD match the name of the bundle (without 13 | // the extension) 14 | // 15 | // ----------------------------------------------------------------------- 16 | // Default : (required) 17 | // Type : string 18 | // 19 | "name" : "bundle-name", 20 | 21 | // 22 | // -= profile =- 23 | // 24 | // The name of jbb profile to use for compiling the contents of this bundle. 25 | // The profile provides the loader functions and the binary specifications 26 | // for the contents. Refer to the bundle you are interested in for more 27 | // details. 28 | // 29 | // The name of the bundle should not include the 'jbb-bndle-' prefix. For 30 | // example, specify 'three' for 'jbb-profile-three'. 31 | // 32 | // If this parameter is missing the profile must be specified at 33 | // compile-time. 34 | // 35 | // ----------------------------------------------------------------------- 36 | // Default : "" 37 | // Type : string 38 | // 39 | "profile" : "three", 40 | 41 | // 42 | // -= imports =- 43 | // 44 | // Other bundles this bundle depends on. These bundles will be loaded 45 | // before loading the contents of this bundle. 46 | // 47 | // It's important to provide this information if your bundle has external 48 | // references, in order to properly resolve them at loading time. 49 | // 50 | // You can specify the bundle names as an array of strings. In this case, 51 | // the compiler will try to locate them in the 'path' directory specified 52 | // at compile-time: 53 | // 54 | // "imports": [ "bundle-1", "bundle-2" ] 55 | // 56 | // If you want more control, you can provide the path to the bundle (without 57 | // the extension) yourself: 58 | // 59 | // "imports": { 60 | // "bundle-1": "../bundle-1", 61 | // "bundle-2": "bundles/bundle-2" 62 | // } 63 | // 64 | // ----------------------------------------------------------------------- 65 | // Default : [] 66 | // Type : array | object 67 | // 68 | "imports" : [ "dependant-module-1", "dependant-module-2" ], 69 | 70 | // 71 | // -= exports =- 72 | // 73 | // This section contains the information for what resources are exposed 74 | // by this bundle. You will need to specify the loader class to use for 75 | // each class of resources. 76 | // 77 | // ----------------------------------------------------------------------- 78 | // Default : (required) 79 | // Type : object 80 | // 81 | "exports" : { 82 | 83 | // 84 | // -= (Blob Embeds) =- 85 | // 86 | // Resources that cannot be serialized by the JBB compiler (such as 87 | // images, sounds or other binary resources) can be placed in the 88 | // "blob" (case-insensitive) group. 89 | // 90 | // These files are just embedded as-is in the final bundle and do not 91 | // benefit by any optimisation. 92 | // 93 | "blob": { 94 | 95 | // 96 | // For each binary resource the MIME type will be 97 | // automatically detected base on the extension 98 | // 99 | "background-audio": "sounds/background.mp3", 100 | 101 | // 102 | // You can also override the automatic MIME detection 103 | // by specifying the MIME type yourself 104 | // 105 | "player-sound": [ "sounds/player.mp3", "audio/mpeg" ] 106 | 107 | }, 108 | 109 | // 110 | // -= (Loader Class) =- 111 | // 112 | // Specify the loader class that is capapble of loading the resources 113 | // in this group. 114 | // 115 | // This key is profile-specific, therefore you should refer to the 116 | // appropriate jbb profile documentation for more details. 117 | // 118 | "THREE.JSONLoader": { 119 | 120 | // 121 | // -= (Resources) =- 122 | // 123 | // Here you specify your actual resources and the key they will 124 | // be available in the bundle: 125 | // 126 | // "key" : "path/to/resource" 127 | // 128 | // Each loader accepts different kind of parameters, so refer to 129 | // the jbb profile for more details. However in most of the cases 130 | // this is just a string pointing to the resources, relative to 131 | // the bundle folder. 132 | // 133 | // On a more complicated scenarios, the macro ${BUNDLE} is 134 | // provided for convenience, and it expands to the base directory 135 | // of the bundle. Refer on the THREE.MD2CharacterLoader example 136 | // below for an example. 137 | // 138 | "test-model": "${BUNDLE}/models/test.json", 139 | 140 | // 141 | // If the ${BUNDLE} macro is not used, the path to the resources 142 | // should be relative to the bundle directory 143 | // 144 | "another-model": "modeuls/world.json", 145 | 146 | // 147 | // There is no limitation on how many resources you can export 148 | // 149 | "big-model": "models/big.json", 150 | "strong-model": "models/strong.json", 151 | "tall-model": "models/tall.json", 152 | "short-model": "models/short.json", 153 | "thin-model": "models/thin.json", 154 | ... 155 | 156 | }, 157 | 158 | // 159 | // Other loaders require more complex configuration 160 | // rather than just a filename. 161 | // 162 | "THREE.MD2CharacterLoader": { 163 | 164 | // 165 | // For example, the MD2CharacterLoader requires detailed 166 | // configuration for various parts (hence the need for // the ${BUNDLE} macro) 167 | // 168 | "ratamahatta": { 169 | "baseUrl": "${BUNDLE}/mesh/ratamahatta/", 170 | "body": "ratamahatta.md2", 171 | "skins": [ "ratamahatta.png", "ctf_b.png", "ctf_r.png", "dead.png", "gearwhore.png" ], 172 | "weapons": [ [ "weapon.md2", "weapon.png" ], 173 | [ "w_bfg.md2", "w_bfg.png" ], 174 | [ "w_blaster.md2", "w_blaster.png" ], 175 | [ "w_chaingun.md2", "w_chaingun.png" ], 176 | [ "w_glauncher.md2", "w_glauncher.png" ], 177 | [ "w_hyperblaster.md2", "w_hyperblaster.png" ], 178 | [ "w_machinegun.md2", "w_machinegun.png" ], 179 | [ "w_railgun.md2", "w_railgun.png" ], 180 | [ "w_rlauncher.md2", "w_rlauncher.png" ], 181 | [ "w_shotgun.md2", "w_shotgun.png" ], 182 | [ "w_sshotgun.md2", "w_sshotgun.png" ] 183 | ] 184 | } 185 | 186 | } 187 | 188 | } 189 | } 190 | ``` -------------------------------------------------------------------------------- /doc/jbb-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/jbb-logo.png -------------------------------------------------------------------------------- /doc/table_BUF_TYPE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_BUF_TYPE.png -------------------------------------------------------------------------------- /doc/table_CHUNK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_CHUNK.png -------------------------------------------------------------------------------- /doc/table_DWS_TYPE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_DWS_TYPE.png -------------------------------------------------------------------------------- /doc/table_LEN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_LEN.png -------------------------------------------------------------------------------- /doc/table_LN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_LN.png -------------------------------------------------------------------------------- /doc/table_NUM_TYPE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_NUM_TYPE.png -------------------------------------------------------------------------------- /doc/table_OP_ARR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OP_ARR.png -------------------------------------------------------------------------------- /doc/table_OP_BULK_KNOWN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OP_BULK_KNOWN.png -------------------------------------------------------------------------------- /doc/table_OP_CTL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OP_CTL.png -------------------------------------------------------------------------------- /doc/table_OP_OBJECT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OP_OBJECT.png -------------------------------------------------------------------------------- /doc/table_OP_PRIM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OP_PRIM.png -------------------------------------------------------------------------------- /doc/table_OT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_OT.png -------------------------------------------------------------------------------- /doc/table_SCALE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_SCALE.png -------------------------------------------------------------------------------- /doc/table_S_TYPE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/doc/table_S_TYPE.png -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var header = require('gulp-header'); 3 | var jbb_profile = require('gulp-jbb-profile'); 4 | var webpack = require('webpack-stream'); 5 | var PROD = JSON.parse(process.env.PROD_DEV || "0"); 6 | 7 | // 8 | // Compile test files 9 | // 10 | gulp.task('test/simple-profile', function() { 11 | return gulp 12 | .src([ 'test/simple-profile/specs.yaml' ]) 13 | .pipe(jbb_profile()) 14 | .pipe(gulp.dest('test/simple-profile')); 15 | }); 16 | gulp.task('test/second-profile', function() { 17 | return gulp 18 | .src([ 'test/simple-profile/second.yaml' ]) 19 | .pipe(jbb_profile()) 20 | .pipe(gulp.dest('test/simple-profile')); 21 | }); 22 | 23 | // 24 | // Compile the binary loader 25 | // 26 | gulp.task('dist/jbb', function() { 27 | return gulp.src('decoder.js') 28 | .pipe(webpack({ 29 | module: { 30 | loaders: [ 31 | { test: /\.json$/, loader: 'json' }, 32 | ], 33 | }, 34 | node: { 35 | 'fs': 'empty' 36 | }, 37 | output: { 38 | // The output filename 39 | filename: PROD ? 'jbb.min.js' : 'jbb.js', 40 | // Export itself to a global var 41 | libraryTarget: 'var', 42 | // Name of the global var: 'JBB.BinaryLoader' 43 | library: [ 'JBB', 'BinaryLoader' ] 44 | }, 45 | externals: { 46 | 'three': 'THREE', 47 | }, 48 | plugins: ([ 49 | new webpack.webpack.optimize.DedupePlugin(), 50 | new webpack.webpack.DefinePlugin({ 51 | GULP_BUILD : PROD 52 | }) 53 | ]).concat(PROD ? [ 54 | new webpack.webpack.optimize.UglifyJsPlugin({ 55 | minimize: true 56 | }) 57 | ] : []) 58 | })) 59 | .pipe(header("/* JBB Binary Bundle Loader - https://github.com/wavesoft/jbb */\n")) 60 | .pipe(gulp.dest('dist')); 61 | }); 62 | 63 | // 64 | // Compile the source loader 65 | // 66 | gulp.task('dist/jbb-loader', function() { 67 | return gulp.src('loader.js') 68 | .pipe(webpack({ 69 | module: { 70 | loaders: [ 71 | { test: /\.json$/, loader: 'json' }, 72 | ], 73 | }, 74 | node: { 75 | 'fs': 'empty' 76 | }, 77 | output: { 78 | // The output filename 79 | filename: PROD ? 'jbb-loader.min.js' : 'jbb-loader.js', 80 | // Export itself to a global var 81 | libraryTarget: 'var', 82 | // Name of the global var: 'Foo' 83 | library: [ 'JBB', 'SourceLoader' ] 84 | }, 85 | externals: { 86 | 'three': 'THREE', 87 | }, 88 | plugins: ([ 89 | new webpack.webpack.optimize.DedupePlugin(), 90 | new webpack.webpack.DefinePlugin({ 91 | GULP_BUILD : PROD 92 | }) 93 | ]).concat(PROD ? [ 94 | new webpack.webpack.optimize.UglifyJsPlugin({ 95 | minimize: true 96 | }) 97 | ] : []) 98 | })) 99 | .pipe(header("/* JBB Source Bundle Loader - https://github.com/wavesoft/jbb */\n")) 100 | .pipe(gulp.dest('dist')); 101 | }); 102 | 103 | // The files to pack on dist release 104 | gulp.task('dist', [ 105 | 'dist/jbb', 106 | 'dist/jbb-loader' 107 | ]); 108 | 109 | 110 | // By default run only script 111 | gulp.task('default', ['dist']); 112 | 113 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * THREE Bundles - Binary Encoder 4 | * Copyright (C) 2015 Ioannis Charalampidis 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License along 17 | * with this program; if not, write to the Free Software Foundation, Inc., 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | * 20 | * @author Ioannis Charalampidis / https://github.com/wavesoft 21 | */ 22 | 23 | /** 24 | * Export binary encoder 25 | */ 26 | module.exports = { 27 | 28 | // Expose binary encoder 29 | 'BinaryEncoder' : require("./encoder.js"), 30 | 31 | // Expose binary decoder 32 | 'BinaryDecoder': require("./decoder.js") 33 | 34 | }; 35 | -------------------------------------------------------------------------------- /lib/BinaryBundle.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * JBB - Javascript Binary Bundles - Binary Decoder 4 | * Copyright (C) 2015 Ioannis Charalampidis 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | /** 22 | * Numerical types 23 | */ 24 | var NUMTYPE = { 25 | UINT8: 0, INT8: 1, 26 | UINT16: 2, INT16: 3, 27 | UINT32: 4, INT32: 5, 28 | FLOAT32: 6, FLOAT64: 7 29 | } 30 | 31 | ////////////////////////////////////////////////////////////////// 32 | // Local Helper Functions 33 | ////////////////////////////////////////////////////////////////// 34 | 35 | /** 36 | * Create a weave property factory 37 | */ 38 | function genWeavePropFn( d ) { 39 | var code = ""; 40 | for (var i=0; i> 8; 116 | 117 | // Calculate upper bounds 118 | this.max64 = hv32[2]; 119 | this.max32 = hv32[3]; 120 | this.max16 = hv32[4]; 121 | this.max8 = hv32[5]; 122 | this.lenST = hv32[6]; 123 | this.lenOT = hv32[7]; 124 | 125 | // Validate magic 126 | if (this.magic == 0x3142) { 127 | throw { 128 | 'name' : 'EndianessError', 129 | 'message' : 'Unfortunately the JBB format is currently only compatible with Little-Endian CPUs', 130 | toString : function(){return this.name + ": " + this.message;} 131 | } 132 | } else if (this.magic != 0x4231) { 133 | throw { 134 | 'name' : 'DecodingError', 135 | 'message' : 'This does not look like a JBB archive! (Magic is 0x'+this.magic.toString(16)+')', 136 | toString : function(){return this.name + ": " + this.message;} 137 | } 138 | } 139 | 140 | // Validate bundle version number 141 | if (this.version != 0x0102) { 142 | throw { 143 | 'name' : 'DecodingError', 144 | 'message' : 'Unsupported bundle version v'+this.ver_minor+'.'+this.ver_minor, 145 | toString : function(){return this.name + ": " + this.message;} 146 | } 147 | } 148 | 149 | // Setup indices 150 | if (this.sparse) { 151 | 152 | // Setup indices 153 | this.i64 = 0; 154 | this.i32 = 0; 155 | this.i16 = 0; 156 | this.i8 = header_size; 157 | this.iEnd= this.i8 + this.max8 - this.lenST; 158 | 159 | // Offsets of array beginning (for getting array portions) 160 | this.ofs8 = this.i8; 161 | this.ofs16 = this.i16; 162 | this.ofs32 = this.i32; 163 | this.ofs64 = this.i64; 164 | 165 | } else { 166 | 167 | // Setup indices 168 | this.i64 = header_size; 169 | this.i32 = this.i64 + this.max64; 170 | this.i16 = this.i32 + this.max32; 171 | this.i8 = this.i16 + this.max16; 172 | this.iEnd= this.i8 + this.max8 - this.lenST; 173 | 174 | // Offsets of array beginning (for getting array portions) 175 | this.ofs8 = this.i8; 176 | this.ofs16 = this.i16; 177 | this.ofs32 = this.i32; 178 | this.ofs64 = this.i64; 179 | 180 | // Convert to element index 181 | this.i16 /= 2; 182 | this.i32 /= 4; 183 | this.i64 /= 8; 184 | 185 | } 186 | 187 | // Current position of i16 used later 188 | var ci16 = this.i16; 189 | 190 | // Load object tables 191 | var ots = []; 192 | if (this.table_count <= 1) { 193 | 194 | // The bundle can have no profile if needed 195 | if (this.table_id !== 0x00) 196 | ots.push( this.table_id ); 197 | 198 | } else if (this.table_id !== 0x00) { 199 | throw { 200 | 'name' : 'DecodingError', 201 | 'message' : 'Invalid header data', 202 | toString : function(){return this.name + ": " + this.message;} 203 | } 204 | } else { 205 | // Read known table IDs 206 | for (var i=0; i 0) { 221 | for (var l=this.i8+this.max8, i=l-this.lenST; i 0) { 239 | for (var l=ci16*2+this.max16, i=l-this.lenOT; i= this.string_table.length) throw { 330 | 'name' : 'RangeError', 331 | 'message' : 'String ID is outside than the range of the string lookup table!', 332 | toString : function(){return this.name + ": " + this.message;} 333 | } 334 | return this.string_table[id]; 335 | } 336 | 337 | /** 338 | * Check if we ran out of opcodes 339 | */ 340 | BinaryBundle.prototype.eof = function() { 341 | return (this.i8 >= this.iEnd); 342 | } 343 | 344 | /** 345 | * Print the index offsets 346 | */ 347 | BinaryBundle.prototype.where = function() { 348 | console.log( "i8=", this.i8 - this.ofs8, " [U:",this.u8[this.i8], "0x"+this.u8[this.i8].toString(16),"/ S:",this.s8[this.i8],"]" ); 349 | console.log( "i16=", this.i16 - this.ofs16/2, " [U:",this.u16[this.i16], "/ S:",this.s16[this.i16],"]" ); 350 | console.log( "i32=", this.i32 - this.ofs32/4, " [U:",this.u32[this.i32], "/ S:",this.s32[this.i32],"/ F:",this.f32[this.i32],"]" ); 351 | console.log( "i64=", this.i64 - this.ofs64/8, " [F:",this.f64[this.i64], "]"); 352 | } 353 | 354 | module.exports = BinaryBundle; 355 | -------------------------------------------------------------------------------- /lib/BinaryStream.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * JBB - Javascript Binary Bundles - Binary Stream Class 4 | * Copyright (C) 2015 Ioannis Charalampidis 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | var fs = require("fs"); 22 | var util = require("util"); 23 | 24 | ////////////////////////////////////////////////////////////////// 25 | // Binary Stream 26 | ////////////////////////////////////////////////////////////////// 27 | 28 | /** 29 | * Binary Stream 30 | */ 31 | var BinaryStream = function( filename, alignSize, logWrites ) { 32 | 33 | // Local properties 34 | this.offset = 0; 35 | this.blockSize = 1024 * 16; 36 | this.logWrites = logWrites; 37 | 38 | // Private properties 39 | this.__writeChunks = []; 40 | this.__syncOffset = 0; 41 | this.__fd = null; 42 | this.__alignSize = 0; 43 | 44 | // Initialize 45 | this.__alignSize = alignSize || 8; 46 | this.__fd = fs.openSync( filename, 'w+' ); 47 | 48 | } 49 | 50 | /** 51 | * Prototype constructor 52 | */ 53 | BinaryStream.prototype = { 54 | 55 | 'constructor': BinaryStream, 56 | 57 | /** 58 | * Finalise and close stream 59 | */ 60 | 'close': function() { 61 | // Close 62 | fs.closeSync( this.__fd ); 63 | }, 64 | 65 | /** 66 | * Finalize the stream 67 | */ 68 | 'finalize': function() { 69 | // Write alignment padding 70 | var alignOffset = this.offset % this.__alignSize; 71 | if (alignOffset > 0) 72 | this.write( new Buffer(new Uint8Array( this.__alignSize - alignOffset )) ); 73 | // Synchronize 74 | this.__sync( true ); 75 | }, 76 | 77 | /** 78 | * Write a buffer using the compile function 79 | */ 80 | 'write': function( buffer ) { 81 | if (this.logWrites) { 82 | var bits = String(this.__alignSize*8); 83 | if (bits.length === 1) bits=" "+bits; 84 | console.log((bits+"b ").yellow+("@"+this.offset/this.__alignSize).bold.yellow+": "+util.inspect(buffer)); 85 | } 86 | this.__writeChunks.push( buffer ); 87 | this.offset += buffer.length; 88 | this.__sync(); 89 | }, 90 | 91 | /** 92 | * Write an array of buffers 93 | */ 94 | 'writeMany': function( buffers ) { 95 | var bits; 96 | for (var i=0, bl=buffers.length; i 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | /** 22 | * Encoder profile 23 | */ 24 | var DecodeProfile = function() { 25 | this.size = 0; 26 | this._lib = []; 27 | this._profiles = []; 28 | this._offset = 0; 29 | this._foffset = 0; 30 | }; 31 | 32 | /** 33 | * Combine current profile with the given one 34 | */ 35 | DecodeProfile.prototype.add = function( profile ) { 36 | // Add in the library. They will be used 37 | // only when explicitly requested. 38 | this._lib.push( profile ); 39 | } 40 | 41 | /** 42 | * From the profiles added, use the ones specified in the list, 43 | * in the order they were given. 44 | */ 45 | DecodeProfile.prototype.use = function( ids ) { 46 | 47 | // If empty profile, return 48 | if (ids.length == 0) 49 | return; 50 | 51 | // Pick the profiles of interest 52 | for (var i=0, l=ids.length; i> 4, rev = id & 0xF; 67 | throw { 68 | 'name' : 'DecodingError', 69 | 'message' : 'The bundle uses profile 0x'+pid.toString(16)+', rev '+rev+' but it was not loaded!', 70 | toString : function(){return this.name + ": " + this.message;} 71 | } 72 | } 73 | 74 | // Update size 75 | this.size += profile.size; 76 | 77 | // Keep profiles and bounds for fast segment lookup 78 | this._profiles.push([ profile, 79 | this._foffset, this._foffset + profile.frequent, 80 | this._offset, this._offset + profile.size - profile.frequent ]); 81 | 82 | // Update offsets 83 | this._offset += profile.size - profile.frequent; 84 | this._foffset += profile.frequent; 85 | 86 | } 87 | 88 | } 89 | 90 | /** 91 | * Drop-in replacement for the profile decode function 92 | */ 93 | DecodeProfile.prototype.decode = function( eid ) { 94 | if (eid < 32) { 95 | for (var i=0, l=this._profiles.length; i= ofs) && (eid < end)) 98 | return p.decode( eid - ofs ); 99 | } 100 | } else { 101 | eid -= 32; 102 | for (var i=0, l=this._profiles.length; i= ofs) && (eid < end)) 105 | return p.decode( eid - ofs + 32 ); 106 | } 107 | } 108 | }; 109 | 110 | // Export profile 111 | module.exports = DecodeProfile; 112 | -------------------------------------------------------------------------------- /lib/EncodeProfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * JBB - Javascript Binary Bundles - Encoder Multi-Profile Drop-in replacement 4 | * Copyright (C) 2015 Ioannis Charalampidis 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | var Errors = require("./Errors"); 22 | 23 | /** 24 | * Encoder profile 25 | */ 26 | var EncodeProfile = function() { 27 | this.size = 0; 28 | this.id = 0; 29 | this.count = 0; 30 | this._profiles = []; 31 | this._ioffset = 0; 32 | this._foffset = 0; 33 | }; 34 | 35 | /** 36 | * Combine current profile with the given one 37 | */ 38 | EncodeProfile.prototype.add = function( profile ) { 39 | 40 | // Update size 41 | this.size += profile.size; 42 | 43 | // Keep profiles and their offsets 44 | this._profiles.push([ this._ioffset, this._foffset, profile ]); 45 | this._ioffset += profile.size - profile.frequent; 46 | this._foffset += profile.frequent; 47 | 48 | // Keep the first ID 49 | if (this.id === 0) 50 | this.id = profile.id; 51 | 52 | // Test for overflow 53 | if (this._foffset > 31) { 54 | throw new Errors.AssertError("Exceeded maximum number of frequent profile items"); 55 | } 56 | 57 | // Increment counter 58 | this.count++; 59 | 60 | } 61 | 62 | /** 63 | * Drop-in replacement for the profile encode function 64 | */ 65 | EncodeProfile.prototype.encode = function( entity ) { 66 | for (var i=0, l=this._profiles.length; i 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | /** 22 | * Helper to reduce a few bytes on the minified version 23 | */ 24 | function _define(inst, message, type) { 25 | var temp = Error.call(inst, message); 26 | temp.name = inst.name = type; 27 | inst.stack = temp.stack; 28 | inst.message = temp.message; 29 | } 30 | 31 | /** 32 | * Encode error is a generic error emmited during encoding 33 | */ 34 | var EncodeError = function(message) { 35 | _define(this, message, "EncodeError"); 36 | } 37 | EncodeError.prototype = Object.create(Error.prototype); 38 | 39 | /** 40 | * Assert error is generated by integrity checks 41 | */ 42 | var AssertError = function(message) { 43 | _define(this, message, "AssertError"); 44 | } 45 | AssertError.prototype = Object.create(Error.prototype); 46 | 47 | /** 48 | * Assert error generated by the packing functions 49 | */ 50 | var PackError = function(message) { 51 | _define(this, message, "PackError"); 52 | } 53 | PackError.prototype = Object.create(Error.prototype); 54 | 55 | /** 56 | * Assert error generated by the loading functions 57 | */ 58 | var LoadError = function(message) { 59 | _define(this, message, "LoadError"); 60 | } 61 | LoadError.prototype = Object.create(Error.prototype); 62 | 63 | /** 64 | * A value is out of range 65 | */ 66 | var RangeError = function(message) { 67 | _define(this, message, "RangeError"); 68 | } 69 | RangeError.prototype = Object.create(Error.prototype); 70 | 71 | /** 72 | * Internal referrence error 73 | */ 74 | var IRefError = function(message) { 75 | _define(this, message, "IRefError"); 76 | } 77 | IRefError.prototype = Object.create(Error.prototype); 78 | 79 | /** 80 | * External referrence error 81 | */ 82 | var XRefError = function(message) { 83 | _define(this, message, "XRefError"); 84 | } 85 | XRefError.prototype = Object.create(Error.prototype); 86 | 87 | module.exports = { 88 | 'EncodeError': EncodeError, 89 | 'AssertError': AssertError, 90 | 'PackError': PackError, 91 | 'RangeError': RangeError, 92 | 'IRefError': IRefError, 93 | 'XRefError': XRefError, 94 | }; -------------------------------------------------------------------------------- /lib/ProgressManager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * JBB - Javascript Binary Bundles - Binary Decoder 4 | * Copyright (C) 2015 Ioannis Charalampidis 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | /** 22 | * A partial progress item 23 | */ 24 | var ProgressManagerPart = function(parent) { 25 | this.parent = parent; 26 | this.objectLookup = []; 27 | this.objects = []; 28 | this.value = 0; 29 | }; 30 | 31 | /** 32 | * Update sub-object progress 33 | */ 34 | ProgressManagerPart.prototype.update = function( obj, progress, total ) { 35 | 36 | // Get/Create new object for tracking this object 37 | var id = this.objectLookup.indexOf(obj); 38 | if (id === -1) { 39 | id = this.objectLookup.length; 40 | this.objectLookup.push(obj); 41 | this.objects.push([ progress, total ]) 42 | } else { 43 | this.objects[id][0] = progress; 44 | this.objects[id][1] = total; 45 | } 46 | 47 | // Update summary 48 | var prog=0, tot=0; 49 | for (var i=0; i 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Ioannis Charalampidis / https://github.com/wavesoft 19 | */ 20 | 21 | var toposort = require('toposort'); 22 | var path = require('path'); 23 | var mime = require('mime'); 24 | 25 | var IS_BROWSER = true && (new Function("try {return this===window;}catch(e){ return false;}")()); 26 | 27 | /** 28 | * Pick MIME type according to filename and known MIME Types 29 | */ 30 | function mimeTypeFromFilename( filename ) { 31 | var ext = filename.split(".").pop().toLowerCase(); 32 | return mime.lookup(filename) || "application/octet-stream"; 33 | } 34 | 35 | /** 36 | * State constants for the 37 | */ 38 | const STATE_REQUESTED = 0; 39 | const STATE_SPECS = 1; 40 | const STATE_LOADING = 2; 41 | const STATE_LOADED = 3; 42 | const STATE_FAILED = 4; 43 | 44 | /** 45 | * Apply full path by replacing the ${BUNDLE} macro or 46 | * by prepending the path to the path if config is only a string 47 | */ 48 | function applyFullPath( baseDir, suffix, config, skipRelative ) { 49 | if (typeof(config) == "string") { 50 | // Check for macros 51 | if (config.indexOf('${') >= 0) { 52 | config = config.replace(/\${(.+?)}/g, function(match, contents, offset, s) 53 | { 54 | var key = contents.toLowerCase(); 55 | if (key === "bundle") { 56 | return baseDir 57 | } else if (key === "suffix") { 58 | return suffix; 59 | } else { 60 | console.warn("Unknown macro '"+key+"' encountered in: "+config); 61 | return ""; 62 | } 63 | }); 64 | // Check for full path 65 | } else if (!skipRelative && config.substr(0,1) != "/") { 66 | return path.join(baseDir, config) + suffix; 67 | } 68 | // Otherwise we are good 69 | return config; 70 | } else { 71 | if (config.constructor == ({}).constructor) { 72 | var ans = {}; 73 | for (var k in config) 74 | ans[k] = applyFullPath( baseDir, suffix, config[k], true ); 75 | return ans; 76 | } else if (config.length !== undefined) { 77 | var ans = []; 78 | for (var i=0; i 1) nameParts.pop(); 524 | name = nameParts.join("."); 525 | if (urlparts.length > 1) suffix="?"+urlparts[1]; 526 | url = urlparts[0]; 527 | 528 | // Get/Create bundle queue item 529 | var item = this.__queuedBundle( name ); 530 | if (item.state == STATE_REQUESTED) { 531 | 532 | // Add prefix if needed 533 | if (this.baseURL) 534 | url = this.baseURL + '/' + url; 535 | 536 | // Add bundle suffix if not already exists 537 | if (url.substr(-this.bundleSuffix.length) != this.bundleSuffix) { 538 | url += this.bundleSuffix; 539 | } 540 | 541 | // Set URL 542 | item.setURL( url + suffix ); 543 | 544 | } 545 | 546 | // Register callback 547 | item.addCallback( callback ); 548 | 549 | } 550 | 551 | /** 552 | * Put a bundle in the queue, by it's specifiactions 553 | */ 554 | BundlesLoader.prototype.addBySpecs = function( specs, callback ) { 555 | 556 | // Get/Create bundle queue item 557 | var item = this.__queuedBundle( specs['name'] ); 558 | if (item.state == STATE_REQUESTED) { 559 | item.setSpecs( specs ); 560 | } 561 | 562 | // Register callback 563 | item.addCallback( callback ); 564 | 565 | } 566 | 567 | /** 568 | * Load all bundles in queue 569 | */ 570 | BundlesLoader.prototype.load = function( callback ) { 571 | // Keep callback in loadCallbacks 572 | this.loadCallbacks.push(callback); 573 | // Start loading 574 | this.__process(); 575 | } 576 | 577 | /** 578 | * Load file contents 579 | */ 580 | BundlesLoader.prototype.__loadFileContents = function( url, asBlob, callback ) { 581 | if (!IS_BROWSER /* browser exclude */) { 582 | // Node Code 583 | var fs = require('fs'); 584 | if (asBlob) { 585 | var buf = fs.readFileSync( url ), // Load Buffer 586 | ab = new ArrayBuffer( buf.length ), // Create an ArrayBuffer to fit the data 587 | view = new Uint8Array(ab); // Create an Uint8Array view 588 | 589 | // Copy buffer into view 590 | for (var i = 0; i < buf.length; ++i) 591 | view[i] = buf[i]; 592 | callback(null, view ); 593 | } else { 594 | fs.readFile(url, {encoding: 'utf8'}, callback); 595 | } 596 | return; 597 | } 598 | 599 | // Broswer code 600 | var req = new XMLHttpRequest(), 601 | scope = this; 602 | 603 | // Place request 604 | req.open('GET', url); 605 | if (asBlob) { 606 | req.responseType = "arraybuffer"; 607 | } else { 608 | req.responseType = "text"; 609 | } 610 | req.send(); 611 | 612 | // Wait until the file is loaded 613 | req.onreadystatechange = function () { 614 | if (req.readyState !== 4) return; 615 | callback(null, req.response); 616 | } 617 | }; 618 | 619 | /** 620 | * Get an item from the queue or express interest for a new item 621 | */ 622 | BundlesLoader.prototype.__queuedBundle = function( name ) { 623 | 624 | // Get/Create bundle queue item 625 | var item = this.bundles[name]; 626 | if (!item) { 627 | 628 | // Create new item 629 | item = new QueuedBundle( this, name ); 630 | 631 | // Put on queue 632 | this.bundles[name] = item; 633 | this.queue.push( item ); 634 | 635 | } 636 | 637 | // Return item 638 | return item; 639 | 640 | } 641 | 642 | /** 643 | * Process queue 644 | */ 645 | BundlesLoader.prototype.__process = function() { 646 | var self = this; 647 | 648 | // Check if we have at least one item in 'requested' state 649 | var pendingRequested = false; 650 | for (var i=0; i STATE_SPECS 659 | // ---------------------------------------------------- 660 | // Download bundle specifications for every bundle 661 | // in pending state. 662 | //////////////////////////////////////////////////////// 663 | 664 | // If there are items pending request, download them in parallel 665 | if (pendingRequested) { 666 | 667 | var context = { 'counter': 0 }; 668 | var load_callback = (function( err ) { 669 | // Check fo errors 670 | if (err) { 671 | console.error("Error loading bundle", err); 672 | return; 673 | } 674 | 675 | // When we reached 0, call process again 676 | if (--this.counter == 0) { 677 | setTimeout( self.__process.bind(self), 1 ); 678 | } 679 | }).bind(context); 680 | 681 | // Load all items pending 682 | for (var i=0; i STATE_LOADED 717 | // ---------------------------------------------------- 718 | // Download bundles that are not part of a dependency 719 | // graph in parallel. 720 | //////////////////////////////////////////////////////// 721 | 722 | // If we have bundles without dependencies, load them in parallel 723 | if (nodepBundles.length > 0) { 724 | 725 | var context = { 'counter': 0 }; 726 | var load_callback = (function() { 727 | // When we reached 0, call process again 728 | if (--this.counter == 0) { 729 | setTimeout( self.__process.bind(self), 1 ); 730 | } 731 | }).bind(context); 732 | 733 | // Load all items pending 734 | for (var i=0; i STATE_LOADED 757 | // ---------------------------------------------------- 758 | // Download cross-referenced bundles in the order they 759 | // appear in the dependency graph. 760 | //////////////////////////////////////////////////////// 761 | 762 | var context = { 'bundles': depBundles }; 763 | var load_step = (function() { 764 | 765 | // Get next item 766 | var item = this.bundles.shift(); 767 | if (!item) { 768 | // We are done loading the bundle chain, fire callbacks 769 | for (var i=0; i", 19 | "license": "Apache-2.0", 20 | "bugs": { 21 | "url": "https://github.com/wavesoft/jbb/issues" 22 | }, 23 | "homepage": "https://github.com/wavesoft/jbb#readme", 24 | "devDependencies": { 25 | "colors": "^1.1.2", 26 | "deep-equal": "^1.0.1", 27 | "gulp-header": "^1.7.1", 28 | "gulp-jbb-profile": "^1.0.2", 29 | "jbb-profile-three": "^1.0.1", 30 | "json-loader": "^0.5.4", 31 | "mocha": "^2.3.4", 32 | "mute": "^2.0.6", 33 | "node-getopt": "^0.2.3", 34 | "seed-random": "^2.2.0", 35 | "webpack-stream": "^3.1.0" 36 | }, 37 | "dependencies": { 38 | "binary-search-tree": "^0.2.4", 39 | "colors": "^1.1.2", 40 | "deep-equal": "^1.0.1", 41 | "gulp-jbb-profile": "^1.0.3", 42 | "jbb-profile-three": "^1.0.3", 43 | "mime": "^1.3.4", 44 | "mock-browser": "^0.92.12", 45 | "temp": "^0.8.3", 46 | "toposort": "^1.0.0", 47 | "xmldom": "^0.1.22", 48 | "xmlhttprequest": "^1.8.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/media/animated.jbbsrc/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "animated", 3 | "exports": { 4 | "THREE.JSONLoader": { 5 | "flamingo" : "flamingo.js", 6 | "horse" : "horse.js", 7 | "monster" : "monster/monster.js" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /test/media/animated.jbbsrc/monster/monster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/animated.jbbsrc/monster/monster.jpg -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/ben.js: -------------------------------------------------------------------------------- 1 | { 2 | "materials": { 3 | "gums": { "map_Kd": "James_Mouth_Gum_Lores.jpg" }, 4 | "tongue": { "map_Kd": "James_Tongue_Lores.jpg" }, 5 | "teethbottom": { "Kd": [251, 248, 248] }, 6 | "teethtop": { "Kd": [251, 248, 248] }, 7 | "topeyelashes": { "Kd": [66, 52, 42], "map_Kd": "James_EyeLashTopTran.png", "d": 0.999 }, 8 | "bottomeyelashes": { "Kd": [66, 52, 42], "map_Kd": "James_EyeLashBotTran.png", "d": 0.999 }, 9 | "head": { "map_Kd": "James_Face_Color_Hair_Lores.jpg", "Ks": [25,25,25], "Ns": 70 }, 10 | "eyetrans": { "Kd": [0, 0, 0] }, 11 | "pupil": { "Kd": [1, 1, 1] }, 12 | "iris": { "map_Kd": "James_Eye_Inner_Green.jpg" }, 13 | "eyeball": { "map_Kd": "James_Eye_Green.jpg" }, 14 | "pants": { "map_Kd": "MJeans1TEX_Lores.jpg", "Ks": [30,30,30], "Ns": 20 }, 15 | "tshirt3": { "map_Kd": "MTshirt3TEX_Lores.jpg", "Ks": [30,30,30], "Ns": 20 }, 16 | "skinbody": { "map_Kd": "James_Body_Lores.jpg", "Ks": [25,25,25], "Ns": 70 }, 17 | "fingernails": { "map_Kd": "Nail_Hand_01_Lores.jpg" }, 18 | "soleshoe": { "map_Kd": "MCasShoe1TEX_Lores.jpg" }, 19 | "sole": { "map_Kd": "MCasShoe1TEX_Lores.jpg" }, 20 | "laces": { "map_Kd": "MCasShoe1TEX_Lores.jpg" }, 21 | "bow": { "map_Kd": "MCasShoe1TEX_Lores.jpg" } 22 | }, 23 | "decodeParams": { 24 | "decodeOffsets": [-2533,-149,-6225,0,0,-511,-511,-511], 25 | "decodeScales": [0.000043,0.000043,0.000043,0.000978,0.000978,0.001957,0.001957,0.001957] 26 | }, 27 | "urls": { 28 | "ben.utf8": [ 29 | { "material": "bottomeyelashes", 30 | "attribRange": [0, 130], 31 | "codeRange": [1040, 388, 192] 32 | }, 33 | { "material": "bow", 34 | "attribRange": [1428, 1848], 35 | "codeRange": [16212, 6457, 3224] 36 | }, 37 | { "material": "eyeball", 38 | "attribRange": [22669, 3834], 39 | "codeRange": [53341, 14697, 7284] 40 | }, 41 | { "material": "eyetrans", 42 | "attribRange": [68038, 5248], 43 | "codeRange": [110022, 20180, 9964] 44 | }, 45 | { "material": "fingernails", 46 | "attribRange": [130202, 1023], 47 | "codeRange": [138386, 2669, 1228] 48 | }, 49 | { "material": "gums", 50 | "attribRange": [141055, 1446], 51 | "codeRange": [152623, 5270, 2624] 52 | }, 53 | { "material": "head", 54 | "attribRange": [157893, 2219], 55 | "codeRange": [175645, 8353, 4168] 56 | }, 57 | { "material": "iris", 58 | "attribRange": [183998, 902], 59 | "codeRange": [191214, 3332, 1664] 60 | }, 61 | { "material": "laces", 62 | "attribRange": [194546, 1016], 63 | "codeRange": [202674, 3590, 1792] 64 | }, 65 | { "material": "pants", 66 | "attribRange": [206264, 8200], 67 | "codeRange": [271864, 30625, 15293] 68 | }, 69 | { "material": "pupil", 70 | "attribRange": [302489, 148], 71 | "codeRange": [303673, 581, 288] 72 | }, 73 | { "material": "skinbody", 74 | "attribRange": [304254, 4990], 75 | "codeRange": [344174, 15770, 7830] 76 | }, 77 | { "material": "sole", 78 | "attribRange": [359944, 2588], 79 | "codeRange": [380648, 9345, 4668] 80 | }, 81 | { "material": "soleshoe", 82 | "attribRange": [389993, 3164], 83 | "codeRange": [415305, 10721, 5352] 84 | }, 85 | { "material": "teethbottom", 86 | "attribRange": [426026, 1235], 87 | "codeRange": [435906, 3513, 1656] 88 | }, 89 | { "material": "teethtop", 90 | "attribRange": [439419, 1666], 91 | "codeRange": [452747, 3937, 1816] 92 | }, 93 | { "material": "tongue", 94 | "attribRange": [456684, 845], 95 | "codeRange": [463444, 3162, 1578] 96 | }, 97 | { "material": "topeyelashes", 98 | "attribRange": [466606, 130], 99 | "codeRange": [467646, 388, 192] 100 | }, 101 | { "material": "tshirt3", 102 | "attribRange": [468034, 4283], 103 | "codeRange": [502298, 14470, 7216] 104 | } 105 | ] 106 | } 107 | } -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/ben_dds.js: -------------------------------------------------------------------------------- 1 | { 2 | "materials": { 3 | "gums": { "map_Kd": "dds/James_Mouth_Gum_Lores.dds" }, 4 | "tongue": { "map_Kd": "dds/James_Tongue_Lores.dds" }, 5 | "teethbottom": { "Kd": [251, 248, 248] }, 6 | "teethtop": { "Kd": [251, 248, 248] }, 7 | "topeyelashes": { "Kd": [66, 52, 42], "map_Kd": "dds/James_EyeLashTopTran.dds", "d": 0.999 }, 8 | "bottomeyelashes": { "Kd": [66, 52, 42], "map_Kd": "dds/James_EyeLashBotTran.dds", "d": 0.999 }, 9 | "head": { "map_Kd": "dds/James_Face_Color_Hair_Lores.dds", "Ks": [25,25,25], "Ns": 70 }, 10 | "eyetrans": { "Kd": [0, 0, 0] }, 11 | "pupil": { "Kd": [1, 1, 1] }, 12 | "iris": { "map_Kd": "dds/James_Eye_Inner_Green.dds" }, 13 | "eyeball": { "map_Kd": "dds/James_Eye_Green.dds" }, 14 | "pants": { "map_Kd": "dds/MJeans1TEX_Lores.dds", "Ks": [30,30,30], "Ns": 20 }, 15 | "tshirt3": { "map_Kd": "dds/MTshirt3TEX_Lores.dds", "Ks": [30,30,30], "Ns": 20 }, 16 | "skinbody": { "map_Kd": "dds/James_Body_Lores.dds", "Ks": [25,25,25], "Ns": 70 }, 17 | "fingernails": { "map_Kd": "dds/Nail_Hand_01_Lores.dds" }, 18 | "soleshoe": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" }, 19 | "sole": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" }, 20 | "laces": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" }, 21 | "bow": { "map_Kd": "dds/MCasShoe1TEX_Lores.dds" } 22 | }, 23 | "decodeParams": { 24 | "decodeOffsets": [-2533,-149,-6225,0,0,-511,-511,-511], 25 | "decodeScales": [0.000043,0.000043,0.000043,0.000978,0.000978,0.001957,0.001957,0.001957] 26 | }, 27 | "urls": { 28 | "ben.utf8": [ 29 | { "material": "bottomeyelashes", 30 | "attribRange": [0, 130], 31 | "codeRange": [1040, 388, 192] 32 | }, 33 | { "material": "bow", 34 | "attribRange": [1428, 1848], 35 | "codeRange": [16212, 6457, 3224] 36 | }, 37 | { "material": "eyeball", 38 | "attribRange": [22669, 3834], 39 | "codeRange": [53341, 14697, 7284] 40 | }, 41 | { "material": "eyetrans", 42 | "attribRange": [68038, 5248], 43 | "codeRange": [110022, 20180, 9964] 44 | }, 45 | { "material": "fingernails", 46 | "attribRange": [130202, 1023], 47 | "codeRange": [138386, 2669, 1228] 48 | }, 49 | { "material": "gums", 50 | "attribRange": [141055, 1446], 51 | "codeRange": [152623, 5270, 2624] 52 | }, 53 | { "material": "head", 54 | "attribRange": [157893, 2219], 55 | "codeRange": [175645, 8353, 4168] 56 | }, 57 | { "material": "iris", 58 | "attribRange": [183998, 902], 59 | "codeRange": [191214, 3332, 1664] 60 | }, 61 | { "material": "laces", 62 | "attribRange": [194546, 1016], 63 | "codeRange": [202674, 3590, 1792] 64 | }, 65 | { "material": "pants", 66 | "attribRange": [206264, 8200], 67 | "codeRange": [271864, 30625, 15293] 68 | }, 69 | { "material": "pupil", 70 | "attribRange": [302489, 148], 71 | "codeRange": [303673, 581, 288] 72 | }, 73 | { "material": "skinbody", 74 | "attribRange": [304254, 4990], 75 | "codeRange": [344174, 15770, 7830] 76 | }, 77 | { "material": "sole", 78 | "attribRange": [359944, 2588], 79 | "codeRange": [380648, 9345, 4668] 80 | }, 81 | { "material": "soleshoe", 82 | "attribRange": [389993, 3164], 83 | "codeRange": [415305, 10721, 5352] 84 | }, 85 | { "material": "teethbottom", 86 | "attribRange": [426026, 1235], 87 | "codeRange": [435906, 3513, 1656] 88 | }, 89 | { "material": "teethtop", 90 | "attribRange": [439419, 1666], 91 | "codeRange": [452747, 3937, 1816] 92 | }, 93 | { "material": "tongue", 94 | "attribRange": [456684, 845], 95 | "codeRange": [463444, 3162, 1578] 96 | }, 97 | { "material": "topeyelashes", 98 | "attribRange": [466606, 130], 99 | "codeRange": [467646, 388, 192] 100 | }, 101 | { "material": "tshirt3", 102 | "attribRange": [468034, 4283], 103 | "codeRange": [502298, 14470, 7216] 104 | } 105 | ] 106 | } 107 | } -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heavy", 3 | "exports": { 4 | "THREE.UTF8Loader": { 5 | "hand": "hand.js", 6 | "ben": "ben_dds.js" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Body_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Body_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_EyeLashBotTran.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_EyeLashBotTran.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_EyeLashTopTran.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_EyeLashTopTran.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Eye_Green.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Eye_Green.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Eye_Inner_Green.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Eye_Inner_Green.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Face_Color_Hair_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Face_Color_Hair_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Mouth_Gum_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Mouth_Gum_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/James_Tongue_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/James_Tongue_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/MCasShoe1TEX_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/MCasShoe1TEX_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/MJeans1TEX_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/MJeans1TEX_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/MTshirt3TEX_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/MTshirt3TEX_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/dds/Nail_Hand_01_Lores.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/dds/Nail_Hand_01_Lores.dds -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/hand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/heavy.jbbsrc/hand.jpg -------------------------------------------------------------------------------- /test/media/heavy.jbbsrc/hand.js: -------------------------------------------------------------------------------- 1 | { 2 | "materials": { 3 | "preview": { "Kd": [184, 136, 234] }, 4 | "nails": { "Kd": [251, 238, 209], "map_Kd": "hand.jpg", "Ks": [30,30,30], "Ns": 100 }, 5 | "skin": { "Kd": [207, 181, 161], "map_Kd": "hand.jpg", "Ks": [30,30,30], "Ns": 30 } 6 | }, 7 | "decodeParams": { 8 | "decodeOffsets": [-7473,-239,-8362,0,0,-511,-511,-511], 9 | "decodeScales": [0.000050,0.000050,0.000050,0.000978,0.000978,0.001957,0.001957,0.001957] 10 | }, 11 | "urls": { 12 | "hand.utf8": [ 13 | { "material": "nails", 14 | "attribRange": [0, 261], 15 | "codeRange": [2088, 817, 404] 16 | }, 17 | { "material": "preview", 18 | "attribRange": [2905, 688], 19 | "codeRange": [8409, 2570, 1280] 20 | }, 21 | { "material": "skin", 22 | "attribRange": [10979, 8899], 23 | "codeRange": [82171, 31026, 15451] 24 | } 25 | ] 26 | } 27 | } -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "md2", 3 | "exports": { 4 | "THREE.MD2CharacterLoader": { 5 | "ratamahatta": { 6 | "baseUrl": "${BUNDLE}/ratamahatta/", 7 | "body": "ratamahatta.md2", 8 | "skins": [ "ratamahatta.png", "ctf_b.png", "ctf_r.png", "dead.png", "gearwhore.png" ], 9 | "weapons": [ [ "weapon.md2", "weapon.png" ] 10 | ] 11 | } 12 | }, 13 | "THREE.TextureLoader": { 14 | "images/ground": "grasslight-big.jpg" 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/grasslight-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/grasslight-big.jpg -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/ratamahatta.md2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/ratamahatta.md2 -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/ctf_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/ctf_b.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/ctf_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/ctf_r.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/dead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/dead.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/gearwhore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/gearwhore.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/ratamahatta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/ratamahatta.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/skins/weapon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/skins/weapon.png -------------------------------------------------------------------------------- /test/media/md2.jbbsrc/ratamahatta/weapon.md2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/md2.jbbsrc/ratamahatta/weapon.md2 -------------------------------------------------------------------------------- /test/media/obj.jbbsrc/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obj", 3 | "exports": { 4 | "THREE.BinaryLoader": { 5 | "obj/walthead": "walt/WaltHead_bin.js" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /test/media/obj.jbbsrc/walt/WaltHead_bin.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wavesoft/jbb/46c40e0bca02d74201fb99925843cde49e9fe68a/test/media/obj.jbbsrc/walt/WaltHead_bin.bin -------------------------------------------------------------------------------- /test/media/obj.jbbsrc/walt/WaltHead_bin.js: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "metadata" : 4 | { 5 | "formatVersion" : 3.1, 6 | "sourceFile" : "WaltHead.obj", 7 | "generatedBy" : "OBJConverter", 8 | "vertices" : 8146, 9 | "faces" : 16160, 10 | "normals" : 8146, 11 | "uvs" : 0, 12 | "materials" : 1 13 | }, 14 | 15 | "materials": [ { 16 | "DbgColor" : 15658734, 17 | "DbgIndex" : 0, 18 | "DbgName" : "lambert2SG.001", 19 | "colorAmbient" : [0.0, 0.0, 0.0], 20 | "colorDiffuse" : [0.64, 0.64, 0.64], 21 | "colorSpecular" : [0.25, 0.25, 0.25], 22 | "illumination" : 2, 23 | "opticalDensity" : 1.0, 24 | "specularCoef" : 92.156863, 25 | "opacity" : 1.0 26 | }], 27 | 28 | "buffers": "WaltHead_bin.bin" 29 | 30 | } 31 | -------------------------------------------------------------------------------- /test/media/vrml.jbbsrc/bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vrml", 3 | "exports": { 4 | "THREE.VRMLLoader": { 5 | "house": "house.wrl" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /test/simple-profile/objects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * THREE Bundles - Binary Encoder/Decoder Test Suite 3 | * Copyright (C) 2015 Ioannis Charalampidis 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * @author Ioannis Charalampidis / https://github.com/wavesoft 20 | */ 21 | 22 | // A coule of local objects, part of Object Table 23 | var ObjectA = function() { 24 | this.objApropA = 125; 25 | this.objApropB = 65532; 26 | this.objApropC = "A string"; 27 | }; 28 | var ObjectB = function( propA, propB ) { 29 | this.objBpropA = propA; 30 | this.objBpropB = propB; 31 | this.objBpropC = new Uint16Array([ 1, 4, 120, 4123 ]); 32 | }; 33 | var ObjectC = function( propA, propB ) { 34 | this.objCpropA = propA; 35 | this.objCpropB = propB; 36 | this.objCpropC = { 'some': { 'sub': 'object' } }; 37 | this.objCpropD = "I am ignored :("; 38 | }; 39 | var ObjectD = function() { 40 | this.objDpropA = 0; 41 | this.objDpropB = 1; 42 | this.objDpropC = "Not part of ObjectTable"; 43 | }; 44 | 45 | // Some more objects for the second object table 46 | var ObjectE = function() { 47 | this.objEpropA = 125; 48 | this.objEpropB = 65532; 49 | this.objEpropC = "A string"; 50 | }; 51 | var ObjectF = function( propA, propB ) { 52 | this.objFpropA = propA; 53 | this.objFpropB = propB; 54 | this.objFpropC = new Uint16Array([ 1, 4, 120, 4123 ]); 55 | }; 56 | var ObjectG = function( propA, propB ) { 57 | this.objGpropA = propA; 58 | this.objGpropB = propB; 59 | this.objGpropC = { 'some': { 'sub': 'object' } }; 60 | this.objGpropD = "I am ignored :("; 61 | }; 62 | 63 | 64 | // Export objects 65 | var exports = module.exports = { 66 | 'ObjectA': ObjectA, 67 | 'ObjectB': ObjectB, 68 | 'ObjectC': ObjectC, 69 | 'ObjectD': ObjectD, 70 | 'ObjectE': ObjectE, 71 | 'ObjectF': ObjectF, 72 | 'ObjectG': ObjectG, 73 | }; 74 | module.exports.static = function(scope) { 75 | Object.keys(exports).forEach(function(key,index) { 76 | scope[key] = exports[key]; 77 | }); 78 | }; 79 | -------------------------------------------------------------------------------- /test/simple-profile/second-decode.js: -------------------------------------------------------------------------------- 1 | var OT = require('./objects'); 2 | 3 | /** 4 | * Factory & Initializer of OT.ObjectE 5 | */ 6 | var factory_OT_ObjectE = { 7 | props: 3, 8 | create: function() { 9 | return new OT.ObjectE(); 10 | }, 11 | init: function(inst, props, pagesize, offset) { 12 | inst.objEpropA = props[offset+pagesize*0]; 13 | inst.objEpropB = props[offset+pagesize*1]; 14 | inst.objEpropC = props[offset+pagesize*2]; 15 | } 16 | } 17 | 18 | /** 19 | * Factory & Initializer of OT.ObjectF 20 | */ 21 | var factory_OT_ObjectF = { 22 | props: 3, 23 | create: function() { 24 | return new OT.ObjectF(); 25 | }, 26 | init: function(inst, props, pagesize, offset) { 27 | inst.objFpropA = props[offset+pagesize*0]; 28 | inst.objFpropB = props[offset+pagesize*1]; 29 | inst.objFpropC = props[offset+pagesize*2]; 30 | } 31 | } 32 | 33 | /** 34 | * Factory & Initializer of OT.ObjectG 35 | */ 36 | var factory_OT_ObjectG = { 37 | props: 3, 38 | create: function() { 39 | return Object.create(OT.ObjectG.prototype); 40 | }, 41 | init: function(inst, props, pagesize, offset) { 42 | OT.ObjectG.call(inst, 43 | props[offset+pagesize*0], 44 | props[offset+pagesize*1]); 45 | inst.objGpropC = props[offset+pagesize*2]; 46 | } 47 | } 48 | 49 | module.exports = { 50 | id: 15024, 51 | size: 3, 52 | frequent: 2, 53 | decode: function( id ) { 54 | if (id < 32) { 55 | if (id < 1) { 56 | if (id === 0) 57 | return factory_OT_ObjectE; 58 | } else { 59 | if (id === 1) 60 | return factory_OT_ObjectF; 61 | } 62 | } else { 63 | if (id === 32) 64 | return factory_OT_ObjectG; 65 | } 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /test/simple-profile/second-encode.js: -------------------------------------------------------------------------------- 1 | var OT = require('./objects'); 2 | 3 | /** 4 | * Property getter OT.ObjectE 5 | */ 6 | function getter_OT_ObjectE(inst) { 7 | return [ 8 | inst.objEpropA, 9 | inst.objEpropB, 10 | inst.objEpropC]; 11 | } 12 | 13 | /** 14 | * Property getter OT.ObjectF 15 | */ 16 | function getter_OT_ObjectF(inst) { 17 | return [ 18 | inst.objFpropA, 19 | inst.objFpropB, 20 | inst.objFpropC]; 21 | } 22 | 23 | /** 24 | * Property getter OT.ObjectG 25 | */ 26 | function getter_OT_ObjectG(inst) { 27 | return [ 28 | inst.objGpropA, 29 | inst.objGpropB, 30 | inst.objGpropC]; 31 | } 32 | 33 | 34 | module.exports = { 35 | id: 15024, 36 | size: 3, 37 | frequent: 2, 38 | encode: function( inst ) { 39 | if (inst instanceof OT.ObjectE) { 40 | return [0, getter_OT_ObjectE]; 41 | } else if (inst instanceof OT.ObjectF) { 42 | return [1, getter_OT_ObjectF]; 43 | } else if (inst instanceof OT.ObjectG) { 44 | return [32, getter_OT_ObjectG]; 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /test/simple-profile/second.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Meta 3 | 4 | @uuid: 0x3AB 5 | @require: 6 | OT: ./objects 7 | 8 | # Simple objects 9 | 10 | OT.ObjectE: 11 | frequent: 1 12 | properties: 13 | - objEpropA 14 | - objEpropB 15 | - objEpropC 16 | 17 | OT.ObjectF: 18 | frequent: 1 19 | properties: 20 | - objFpropA 21 | - objFpropB 22 | - objFpropC 23 | 24 | OT.ObjectG: 25 | init: [ objGpropA, objGpropB ] 26 | properties: 27 | - objGpropA 28 | - objGpropB 29 | - objGpropC 30 | -------------------------------------------------------------------------------- /test/simple-profile/specs-decode.js: -------------------------------------------------------------------------------- 1 | var OT = require('./objects'); 2 | 3 | /** 4 | * Factory & Initializer of OT.ObjectA 5 | */ 6 | var factory_OT_ObjectA = { 7 | props: 3, 8 | create: function() { 9 | return new OT.ObjectA(); 10 | }, 11 | init: function(inst, props, pagesize, offset) { 12 | inst.objApropA = props[offset+pagesize*0]; 13 | inst.objApropB = props[offset+pagesize*1]; 14 | inst.objApropC = props[offset+pagesize*2]; 15 | } 16 | } 17 | 18 | /** 19 | * Factory & Initializer of OT.ObjectB 20 | */ 21 | var factory_OT_ObjectB = { 22 | props: 3, 23 | create: function() { 24 | return new OT.ObjectB(); 25 | }, 26 | init: function(inst, props, pagesize, offset) { 27 | inst.objBpropA = props[offset+pagesize*0]; 28 | inst.objBpropB = props[offset+pagesize*1]; 29 | inst.objBpropC = props[offset+pagesize*2]; 30 | } 31 | } 32 | 33 | /** 34 | * Factory & Initializer of OT.ObjectC 35 | */ 36 | var factory_OT_ObjectC = { 37 | props: 3, 38 | create: function() { 39 | return Object.create(OT.ObjectC.prototype); 40 | }, 41 | init: function(inst, props, pagesize, offset) { 42 | OT.ObjectC.call(inst, 43 | props[offset+pagesize*0], 44 | props[offset+pagesize*1]); 45 | inst.objCpropC = props[offset+pagesize*2]; 46 | } 47 | } 48 | 49 | module.exports = { 50 | id: 7760, 51 | size: 3, 52 | frequent: 1, 53 | decode: function( id ) { 54 | if (id < 32) { 55 | if (id === 0) 56 | return factory_OT_ObjectA; 57 | } else { 58 | if (id < 33) { 59 | if (id === 32) 60 | return factory_OT_ObjectB; 61 | } else { 62 | if (id === 33) 63 | return factory_OT_ObjectC; 64 | } 65 | } 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /test/simple-profile/specs-encode.js: -------------------------------------------------------------------------------- 1 | var OT = require('./objects'); 2 | 3 | /** 4 | * Property getter OT.ObjectA 5 | */ 6 | function getter_OT_ObjectA(inst) { 7 | return [ 8 | inst.objApropA, 9 | inst.objApropB, 10 | inst.objApropC]; 11 | } 12 | 13 | /** 14 | * Property getter OT.ObjectB 15 | */ 16 | function getter_OT_ObjectB(inst) { 17 | return [ 18 | inst.objBpropA, 19 | inst.objBpropB, 20 | inst.objBpropC]; 21 | } 22 | 23 | /** 24 | * Property getter OT.ObjectC 25 | */ 26 | function getter_OT_ObjectC(inst) { 27 | return [ 28 | inst.objCpropA, 29 | inst.objCpropB, 30 | inst.objCpropC]; 31 | } 32 | 33 | 34 | module.exports = { 35 | id: 7760, 36 | size: 3, 37 | frequent: 1, 38 | encode: function( inst ) { 39 | if (inst instanceof OT.ObjectA) { 40 | return [0, getter_OT_ObjectA]; 41 | } else if (inst instanceof OT.ObjectB) { 42 | return [32, getter_OT_ObjectB]; 43 | } else if (inst instanceof OT.ObjectC) { 44 | return [33, getter_OT_ObjectC]; 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /test/simple-profile/specs.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Meta 3 | 4 | @uuid: 0x1E5 5 | @require: 6 | OT: ./objects 7 | 8 | # Simple objects 9 | 10 | OT.ObjectA: 11 | frequent: true 12 | properties: 13 | - objApropA 14 | - objApropB 15 | - objApropC 16 | 17 | OT.ObjectB: 18 | properties: 19 | - objBpropA 20 | - objBpropB 21 | - objBpropC 22 | 23 | OT.ObjectC: 24 | init: [ objCpropA, objCpropB ] 25 | properties: 26 | - objCpropA 27 | - objCpropB 28 | - objCpropC 29 | -------------------------------------------------------------------------------- /test/test-three.js: -------------------------------------------------------------------------------- 1 | /** 2 | * THREE Bundles - Binary Encoder/Decoder Test Suite 3 | * Copyright (C) 2015 Ioannis Charalampidis 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * @author Ioannis Charalampidis / https://github.com/wavesoft 20 | */ 21 | 22 | global.PROD = true; 23 | 24 | var assert = require('assert'); 25 | var path = require('path'); 26 | var temp = require('temp'); 27 | var fs = require('fs'); 28 | var mute = require('mute'); 29 | var common = require('./utils/common'); 30 | var compare = require('./utils/compare'); 31 | var JBBSourceLoader = require('../loader'); 32 | var JBBBinaryLoader = require('../decoder'); 33 | var JBBProfileThreeLoader = require('jbb-profile-three/profile-loader'); 34 | var THREEEncodeProfile = require('jbb-profile-three/profile-encode'); 35 | var THREEDecodeProfile = require('jbb-profile-three/profile-decode'); 36 | var BinaryCompiler = require('../compiler'); 37 | require('./utils/common').static(global); 38 | require('./utils/tests').static(global); 39 | 40 | //////////////////////////////////////////////////////////////// 41 | // Initialization 42 | //////////////////////////////////////////////////////////////// 43 | 44 | // Path to media folder 45 | var mediaDir = path.join(__dirname, 'media'); 46 | 47 | // Prepare a fake browser 48 | var MockBrowser = require('mock-browser').mocks.MockBrowser; 49 | var mock = new MockBrowser(); 50 | 51 | // Fake 'self', 'document' and 'window' 52 | global.document = mock.getDocument(), 53 | global.self = MockBrowser.createWindow(), 54 | global.window = global.self; 55 | 56 | // Fake 'XMLHttpRequest' (shall not be used) 57 | global.XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; 58 | 59 | // Fake Blob 60 | global.Blob = function(url) { }; 61 | global.URL = { createObjectURL: function(blob) { return ""; } }; 62 | 63 | // Initialize environment for node.js 64 | JBBProfileThreeLoader.initialize(); 65 | 66 | //////////////////////////////////////////////////////////////// 67 | // Helper Functinos 68 | //////////////////////////////////////////////////////////////// 69 | 70 | /** 71 | * Closure for repeating the test with different bundles 72 | */ 73 | function describe_clojure( bundleName, testFn ) { 74 | return function() { 75 | var srcDB, binDB, tmpFile, sourceLoadingTime, binaryLoadingTime; 76 | 77 | it('should properly load the source bundle', function( done ) { 78 | 79 | /* Load the source bundle */ 80 | var unmute = mute(); 81 | var sourceLoader = new JBBSourceLoader( mediaDir ); 82 | sourceLoadingTime = Date.now(); 83 | sourceLoader.addProfileLoader( JBBProfileThreeLoader ) 84 | sourceLoader.add( bundleName ); 85 | sourceLoader.load(function( err ) { 86 | unmute(); 87 | assert.ok( err == null, err ); 88 | sourceLoadingTime = Date.now() - sourceLoadingTime; 89 | 90 | /* Keep the database for the next test */ 91 | srcDB = sourceLoader.database; 92 | done(); 93 | 94 | }); 95 | 96 | }); 97 | it('should properly compile the bundle', function( done ) { 98 | 99 | /* This can take long time. Give it 2 minutes. */ 100 | this.timeout(120000); 101 | 102 | /* Create a temporary file to compile */ 103 | var unmute = mute(); 104 | tmpFile = path.join(__dirname, bundleName+'.tmp');// temp.path({suffix: '.test'}); 105 | BinaryCompiler.compileFile( path.join(mediaDir, bundleName+'.jbbsrc'), tmpFile, { 106 | 'path' : mediaDir, 107 | 'log' : 0x8000, 108 | 'profileEncoder': THREEEncodeProfile, 109 | 'profileLoader' : JBBProfileThreeLoader, 110 | 'sparse' : false 111 | }, function( err ) { 112 | unmute(); 113 | 114 | tmpFile += ".jbb"; 115 | assert.ok( fs.existsSync( tmpFile ), 'bundle file was not created' ); 116 | assert.ok( err == null, err ); 117 | 118 | /* Loaded */ 119 | done(); 120 | 121 | } 122 | ); 123 | 124 | }); 125 | it('should properly load the binary bundle on time', function( done ) { 126 | 127 | /* Load the source bundle */ 128 | var unmute = mute(); 129 | var binaryLoader = new JBBBinaryLoader( path.dirname(tmpFile) ); 130 | binaryLoadingTime = Date.now(); 131 | binaryLoader.addProfile( THREEDecodeProfile ); 132 | binaryLoader.addByBuffer( common.readChunk(tmpFile) ); 133 | binaryLoader.load(function( err ) { 134 | unmute(); 135 | assert.ok( err == null, err ); 136 | binaryLoadingTime = Date.now() - binaryLoadingTime; 137 | 138 | /* Delete bundle */ 139 | fs.unlink( tmpFile ); 140 | 141 | /* Keep the database for the next test */ 142 | binDB = binaryLoader.database; 143 | done(); 144 | 145 | }); 146 | 147 | }); 148 | it('should be encoded correctly', function(done) { 149 | 150 | /* This is not optimized, can take up to 10 seconds. */ 151 | this.timeout(10000); 152 | 153 | assert.deepEqual( Object.keys(srcDB).sort(), Object.keys(binDB).sort(), 'database keys must be the same' ); 154 | 155 | testFn( srcDB, binDB ); 156 | done(); 157 | 158 | }); 159 | it('should be loaded faster than the source bundle', function(done) { 160 | // Give a tollerance of 5 ms 161 | if (binaryLoadingTime > sourceLoadingTime + 5) { 162 | var percent = 100 * (binaryLoadingTime - sourceLoadingTime) / sourceLoadingTime; 163 | assert.ok( binaryLoadingTime < sourceLoadingTime, 'binary bundle loaded slower (by ' + percent.toFixed(2) + ' %)' ); 164 | } 165 | done(); 166 | }); 167 | 168 | }; 169 | } 170 | 171 | //////////////////////////////////////////////////////////////// 172 | // Test Entry Point 173 | //////////////////////////////////////////////////////////////// 174 | 175 | // Profiled encoding/decoding 176 | describe('[THREE.js Profile Tests]', function() { 177 | 178 | var commonIgnoredKeys = [ 179 | // Functions 180 | 'onChangeCallback', 181 | 'constructor', 182 | 'set', 183 | // OK To change 184 | 'uuid', 185 | 'parent', 186 | // Pointless to compare 187 | 'solid', 188 | 'image', 189 | 'version', 190 | ]; 191 | 192 | describe('animated.jbbsrc', describe_clojure( 'animated', function( original, encoded ) { 193 | 194 | // Configuration for explicit deep equal 195 | var config = { 196 | ignoreKeys: commonIgnoredKeys, 197 | ignoreClasses: [ ], 198 | numericTollerance: 0.0001 199 | }; 200 | 201 | // Explicit deep equal comparison 202 | compare.explicitDeepEqual( original['animated/flamingo'], 203 | encoded['animated/flamingo'], 204 | 'in animated/flamingo', config ); 205 | compare.explicitDeepEqual( original['animated/horse'], 206 | encoded['animated/horse'], 207 | 'in animated/horse', config ); 208 | compare.explicitDeepEqual( original['animated/monster'], 209 | encoded['animated/monster'], 210 | 'in animated/monster', config ); 211 | 212 | })); 213 | 214 | describe('vrml.jbbsrc', describe_clojure( 'vrml', function( original, encoded ) { 215 | 216 | // Configuration for explicit deep equal 217 | var config = { 218 | ignoreKeys: commonIgnoredKeys, 219 | ignoreClasses: [ ], 220 | numericTollerance: 0.001 221 | }; 222 | 223 | // Explicit deep equal comparison 224 | compare.explicitDeepEqual( original['vrml/house'], 225 | encoded['vrml/house'], 226 | 'in vrml/house', config ); 227 | 228 | })); 229 | 230 | describe('md2.jbbsrc', describe_clojure( 'md2', function( original, encoded ) { 231 | 232 | // Configuration for explicit deep equal 233 | var config = { 234 | ignoreKeys: commonIgnoredKeys, 235 | ignoreClasses: [ ], 236 | numericTollerance: 0.001 237 | }; 238 | 239 | // Explicit deep equal comparison 240 | compare.explicitDeepEqual( original['md2/ratamahatta'], 241 | encoded['md2/ratamahatta'], 242 | 'in md2/ratamahatta', config ); 243 | 244 | })); 245 | 246 | describe('heavy.jbbsrc', describe_clojure( 'heavy', function( original, encoded ) { 247 | 248 | // Configuration for explicit deep equal 249 | var config = { 250 | ignoreKeys: commonIgnoredKeys, 251 | ignoreClasses: [ ], 252 | numericTollerance: 0.001 253 | }; 254 | 255 | // Explicit deep equal comparison 256 | compare.explicitDeepEqual( original['heavy/hand'], 257 | encoded['heavy/hand'], 258 | 'in heavy/hand', config ); 259 | 260 | // Explicit deep equal comparison 261 | compare.explicitDeepEqual( original['heavy/ben'], 262 | encoded['heavy/ben'], 263 | 'in heavy/ben', config ); 264 | 265 | })); 266 | 267 | describe('obj.jbbsrc', describe_clojure( 'obj', function( original, encoded ) { 268 | 269 | // Configuration for explicit deep equal 270 | var config = { 271 | ignoreKeys: commonIgnoredKeys, 272 | ignoreClasses: [ ], 273 | numericTollerance: 0.001 274 | }; 275 | 276 | // Explicit deep equal comparison 277 | compare.explicitDeepEqual( original['obj/walthead'], 278 | encoded['obj/walthead'], 279 | 'in obj/walthead', config ); 280 | 281 | })); 282 | 283 | }); 284 | -------------------------------------------------------------------------------- /test/utils/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * THREE Bundles - Binary Encoder/Decoder Test Suite 3 | * Copyright (C) 2015 Ioannis Charalampidis 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * @author Ioannis Charalampidis / https://github.com/wavesoft 20 | */ 21 | 22 | var BinaryEncoder = require("../../encoder.js"); 23 | var BinaryLoader = require("../../decoder.js"); 24 | var temp = require("temp").track(); 25 | var fs = require('fs'); 26 | 27 | /** 28 | * Read filename to buffer 29 | */ 30 | function readChunk( filename ) { 31 | // Read into buffer 32 | var file = fs.readFileSync(filename), 33 | u8 = new Uint8Array(file); 34 | // Return buffer 35 | return u8.buffer; 36 | } 37 | 38 | /** 39 | * Create a binary encoder pointing to a random file 40 | */ 41 | function open_encoder( profile, sparse ) { 42 | // Create a temporary file 43 | var tempName = temp.path({suffix: '.tmp'}); 44 | 45 | // Create an encoder 46 | var encoder = new BinaryEncoder(tempName, { 47 | 'name' : 'test', 48 | 'log' : 0, 49 | 'sparse' : sparse || false 50 | }); 51 | 52 | // Add one or more profiles 53 | if (profile.length) { 54 | for (var i=0; i 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * @author Ioannis Charalampidis / https://github.com/wavesoft 20 | */ 21 | 22 | var assert = require('assert'); 23 | var mute = require('mute'); 24 | 25 | /** 26 | * assert.equal with deep comparion only on some objects 27 | */ 28 | function explicitDeepEqual( expected, actual, message, config, path ) { 29 | var path = path || "object", 30 | config = config || {}, a, b, unmute; 31 | 32 | if ((actual === undefined) || (expected === undefined)) { 33 | if ( ((actual === undefined) && (expected !== undefined)) || 34 | ((actual !== undefined) && (expected === undefined))) { 35 | console.log("- Actual=", actual); 36 | assert.equal( 37 | (actual === undefined) ? '[undefined]' : (typeof actual), 38 | (expected === undefined) ? '[undefined]' : (typeof expected), 39 | path + ' mismatch ' + message ); 40 | } 41 | return; 42 | } 43 | 44 | if (typeof actual === "object") { 45 | assert.equal( "object", typeof expected, path + ': ' + message ); 46 | assert.equal( (actual === null), (expected === null), path + ': ' + message ); 47 | 48 | for (var k in actual) { 49 | 50 | // Get values 51 | unmute = mute(); 52 | try { 53 | a = actual[k]; 54 | b = expected[k]; 55 | } finally { 56 | unmute(); 57 | } 58 | 59 | // Skip ignored keys and classes 60 | if (typeof a === 'function') continue; 61 | if (config.ignoreKeys.indexOf(k) >= 0) continue; 62 | for (var i=0; i config.numericTollerance) { 70 | assert.equal( actual, expected, path + ': ' + message ); 71 | } 72 | } else { 73 | assert.equal( actual, expected, path + ' mismatch ' + message ); 74 | } 75 | } 76 | 77 | // Export functions 78 | var exports = module.exports = { 79 | 'explicitDeepEqual': explicitDeepEqual, 80 | }; 81 | module.exports.static = function(scope) { 82 | Object.keys(exports).forEach(function(key,index) { 83 | scope[key] = exports[key]; 84 | }); 85 | }; 86 | 87 | -------------------------------------------------------------------------------- /test/utils/tests.js: -------------------------------------------------------------------------------- 1 | /** 2 | * THREE Bundles - Binary Encoder/Decoder Test Suite 3 | * Copyright (C) 2015 Ioannis Charalampidis 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | * @author Ioannis Charalampidis / https://github.com/wavesoft 20 | */ 21 | 22 | var util = require('util'); 23 | var assert = require('assert'); 24 | var seed = require('seed-random'); 25 | var compare = require('./compare'); 26 | 27 | require('./common').static(global); 28 | require('../simple-profile/objects').static(global); 29 | 30 | var EncodeProfile = require('../simple-profile/specs-encode'); 31 | var DecodeProfile = require('../simple-profile/specs-decode'); 32 | var EncodeProfile2 = require('../simple-profile/second-encode'); 33 | var DecodeProfile2 = require('../simple-profile/second-decode'); 34 | 35 | var random = seed('jbbtests'); 36 | 37 | //////////////////////////////////////////////////////////////// 38 | // Generator helpers 39 | //////////////////////////////////////////////////////////////// 40 | 41 | /** 42 | * Sequential array generator 43 | */ 44 | function gen_array_seq( typeName, length, min, step ) { 45 | var arr = new global[typeName](length); 46 | for (var i=0, v=min; i> smallest ("+min+","+max+")=",smallest); 62 | for (var i=0; i