diff options
55 files changed, 4339 insertions, 551 deletions
@@ -77,15 +77,7 @@ emcc can be influenced by a few environment variables: import os, sys, shutil, tempfile, subprocess, shlex from subprocess import PIPE, STDOUT from tools import shared - -def execute(cmd, *args, **kw): - try: - return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) - except: - if not isinstance(cmd, str): - cmd = ' '.join(cmd) - print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>' - raise +from tools.shared import Compression, execute, suffix, unsuffixed, unsuffixed_basename # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get @@ -220,9 +212,14 @@ Options that are modified or new in %s include: compiled code asynchronously. Otherwise similar to --embed-file, except that this option is only relevant when generating - HTML (it uses asynchronous binary XHRs). + HTML (it uses asynchronous binary XHRs), + or JS that will be used in a web page. If a directory is passed here, its entire contents will be preloaded. + Preloaded files are stored in filename.data, + where filename.html is the main file you + are compiling to. To run your code, you + will need both the .html and the .data. --compression <codec> Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, @@ -276,6 +273,11 @@ Options that are modified or new in %s include: -v to Clang, and also enable EMCC_DEBUG to details emcc's operations + --remove-duplicates If set, will remove duplicate symbols when + linking. This can be useful because + llvm-link's behavior is not as permissive + as ld is. + The target file, if specified (-o <target>), defines what will be generated: @@ -332,31 +334,17 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS) # ---------------- Utilities --------------- SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc') -BITCODE_SUFFIXES = ('.bc', '.o') +BITCODE_SUFFIXES = ('.bc', '.o', '.obj') DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll') STATICLIB_SUFFIXES = ('.a',) ASSEMBLY_SUFFIXES = ('.ll',) LIB_PREFIXES = ('', 'lib') -IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') -AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3') -AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' } - -def suffix(name): - return name.split('.')[-1] - -def unsuffixed(name): - return '.'.join(name.split('.')[:-1]) - -def unsuffixed_basename(name): - return os.path.basename(unsuffixed(name)) - seen_names = {} -def unsuffixed_uniquename(name): - ret = unsuffixed_basename(name) +def uniquename(name): if name not in seen_names: seen_names[name] = str(len(seen_names)) - return ret + '_' + seen_names[name] + return unsuffixed(name) + '_' + seen_names[name] + (('.' + suffix(name)) if suffix(name) else '') # ---------------- End configs ------------- @@ -411,22 +399,7 @@ else: temp_dir = tempfile.mkdtemp() def in_temp(name): - return os.path.join(temp_dir, name) - -class Compression: - on = False - - @staticmethod - def compressed_name(filename): - return filename + '.compress' - - @staticmethod - def compress(filename): - execute(Compression.encoder, stdin=open(filename, 'rb'), stdout=open(Compression.compressed_name(filename), 'wb')) - - @staticmethod - def worth_it(original, compressed): - return compressed < original - 1500 # save at least one TCP packet or so + return os.path.join(temp_dir, os.path.basename(name)) try: call = CXX if use_cxx else CC @@ -440,14 +413,16 @@ try: llvm_lto = None closure = None js_transform = None - pre_js = None - post_js = None + pre_js = '' + post_js = '' minify_whitespace = None - data_files = [] + preload_files = [] + embed_files = [] compression = None ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') js_libraries = [] + remove_duplicates = False def check_bad_eq(arg): assert '=' not in arg, 'Invalid parameter (do not use "=" with "--" options)' @@ -482,12 +457,12 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--pre-js'): check_bad_eq(newargs[i]) - pre_js = open(newargs[i+1]).read() + pre_js += open(newargs[i+1]).read() + '\n' newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--post-js'): check_bad_eq(newargs[i]) - post_js = open(newargs[i+1]).read() + post_js += open(newargs[i+1]).read() + '\n' newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--minify'): @@ -497,12 +472,12 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--embed-file'): check_bad_eq(newargs[i]) - data_files.append({ 'name': newargs[i+1], 'mode': 'embed' }) + embed_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--preload-file'): check_bad_eq(newargs[i]) - data_files.append({ 'name': newargs[i+1], 'mode': 'preload' }) + preload_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--compression'): @@ -534,6 +509,9 @@ try: js_libraries.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' + elif newargs[i] == '--remove-duplicates': + remove_duplicates = True + newargs[i] = '' newargs = [ arg for arg in newargs if arg is not '' ] if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] @@ -605,6 +583,8 @@ try: libs.append(arg[2:]) newargs[i] = '' + original_input_files = input_files[:] + newargs = [ arg for arg in newargs if arg is not '' ] # Find library files @@ -670,7 +650,7 @@ try: for input_file in input_files: if input_file.endswith(SOURCE_SUFFIXES): if DEBUG: print >> sys.stderr, 'emcc: compiling source file: ', input_file - output_file = in_temp(unsuffixed_uniquename(input_file) + '.o') + output_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') temp_files.append(output_file) args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args) @@ -681,12 +661,12 @@ try: else: # bitcode if input_file.endswith(BITCODE_SUFFIXES): if DEBUG: print >> sys.stderr, 'emcc: copying bitcode file: ', input_file - temp_file = in_temp(unsuffixed_uniquename(input_file) + '.o') + temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') shutil.copyfile(input_file, temp_file) temp_files.append(temp_file) elif input_file.endswith(DYNAMICLIB_SUFFIXES) or shared.Building.is_ar(input_file): if DEBUG: print >> sys.stderr, 'emcc: copying library file: ', input_file - temp_file = in_temp(os.path.basename(input_file)) + temp_file = in_temp(uniquename(input_file)) shutil.copyfile(input_file, temp_file) temp_files.append(temp_file) else: #.ll @@ -694,7 +674,7 @@ try: # Note that by assembling the .ll file, then disassembling it later, we will # remove annotations which is a good thing for compilation time if DEBUG: print >> sys.stderr, 'emcc: assembling assembly file: ', input_file - temp_file = in_temp(unsuffixed_uniquename(input_file) + '.o') + temp_file = in_temp(unsuffixed(uniquename(input_file)) + '.o') shared.Building.llvm_as(input_file, temp_file) temp_files.append(temp_file) @@ -706,25 +686,22 @@ try: print >> sys.stderr, 'emcc: warning: -Ox flags ignored, since not generating JavaScript' if not specified_target: for input_file in input_files: - shutil.move(in_temp(unsuffixed_uniquename(input_file) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix) + shutil.move(in_temp(unsuffixed(uniquename(input_file)) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix) else: if len(input_files) == 1: - shutil.move(in_temp(unsuffixed_uniquename(input_files[0]) + '.o'), specified_target) + shutil.move(in_temp(unsuffixed(uniquename(input_files[0])) + '.o'), specified_target) else: - assert not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + assert len(original_input_files) == 1 or not has_dash_c, 'fatal error: cannot specify -o with -c with multiple files' + str(sys.argv) + ':' + str(original_input_files) # We have a specified target (-o <target>), which is not JavaScript or HTML, and # we have multiple files: Link them if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(temp_files) - shared.Building.link(temp_files, specified_target) + shared.Building.link(temp_files, specified_target, remove_duplicates=remove_duplicates) exit(0) ## Continue on to create JavaScript if DEBUG: print >> sys.stderr, 'emcc: will generate JavaScript' - if final_suffix == 'html': - shared.Settings.GENERATING_HTML = 1 - extra_files_to_link = [] if not LEAVE_INPUTS_RAW and not AUTODEBUG: @@ -764,7 +741,7 @@ try: assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++ with QUANTUM_SIZE == 1' # libcxx might need corrections, so turn them all on. TODO: check which are actually needed shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 - print >> sys.stderr, 'emcc: warning: using libcxx turns on CORRECT_* options' + #print >> sys.stderr, 'emcc: info: using libcxx turns on CORRECT_* options' libcxx_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxx', 'symbols')).readlines()) libcxx_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxx_symbols) libcxx_symbols = set(libcxx_symbols) @@ -776,7 +753,7 @@ try: return os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'libcxxabi', 'libcxxabi.bc') def fix_libcxxabi(): assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++abi with QUANTUM_SIZE == 1' - print >> sys.stderr, 'emcc: warning: using libcxxabi, this may need CORRECT_* options' + #print >> sys.stderr, 'emcc: info: using libcxxabi, this may need CORRECT_* options' #shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 libcxxabi_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols')).readlines()) libcxxabi_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxxabi_symbols) @@ -810,8 +787,7 @@ try: (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): linker_inputs = temp_files + extra_files_to_link if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs - shared.Building.link(linker_inputs, - in_temp(target_basename + '.bc')) + shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), remove_duplicates=remove_duplicates) final = in_temp(target_basename + '.bc') else: if not LEAVE_INPUTS_RAW: @@ -871,216 +847,18 @@ try: if DEBUG: save_intermediate('original') # Embed and preload files - if len(data_files) > 0: + if len(preload_files) + len(embed_files) > 0: if DEBUG: print >> sys.stderr, 'emcc: setting up files' - code = '' - - if final_suffix == 'html': - code += ''' - var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs")); - var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs"); - var hasBlobConstructor; - try { - new Blob(); - hasBlobConstructor = true; - } catch(e) { - hasBlobConstructor = false; - console.log("warning: no blob constructor, cannot create blobs with mimetypes"); - } -''' - - code += 'var preloadedImages = {}; // maps url to image data\n' - code += 'var preloadedAudios = {}; // maps url to audio data\n' - - # Expand directories into individual files - def add(mode, dirname, names): - for name in names: - fullname = os.path.join(dirname, name) - if not os.path.isdir(fullname): - data_files.append({ 'name': fullname, 'mode': mode }) - - for file_ in data_files: - if os.path.isdir(file_['name']): - os.path.walk(file_['name'], add, file_['mode']) - data_files = filter(lambda file_: not os.path.isdir(file_['name']), data_files) - - for file_ in data_files: - file_['name'] = file_['name'].replace(os.path.sep, '/') - file_['net_name'] = file_['name'] - - data_target = unsuffixed(target) + '.data' - - # Set up folders - partial_dirs = [] - for file_ in data_files: - dirname = os.path.dirname(file_['name']) - dirname = dirname.lstrip('/') # absolute paths start with '/', remove that - if dirname != '': - parts = dirname.split('/') - for i in range(len(parts)): - partial = '/'.join(parts[:i+1]) - if partial not in partial_dirs: - code += '''FS.createFolder('/%s', '%s', true, false);\n''' % ('/'.join(parts[:i]), parts[i]) - partial_dirs.append(partial) - - if final_suffix == 'html': - # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead. - data = open(data_target, 'wb') - start = 0 - for file_ in data_files: - file_['data_start'] = start - curr = open(file_['name'], 'rb').read() - file_['data_end'] = start + len(curr) - start += len(curr) - data.write(curr) - data.close() - if Compression.on: - Compression.compress(data_target) - - # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs - code += ''' - function DataRequest() {} - DataRequest.prototype = { - requests: {}, - open: function(mode, name) { - this.requests[name] = this; - }, - send: function() {} - }; - ''' - - counter = 0 - for file_ in data_files: - filename = file_['name'] - if file_['mode'] == 'embed': - # Embed - code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read()))) - elif file_['mode'] == 'preload': - # Preload - assert final_suffix == 'html', 'Can only preload files when generating HTML' - - varname = 'filePreload%d' % counter - counter += 1 - image = filename.endswith(IMAGE_SUFFIXES) - audio = filename.endswith(AUDIO_SUFFIXES) - - if image: - finish = ''' - var bb = new BlobBuilder(); - bb.append(byteArray.buffer); - var b = bb.getBlob(); - var url = URLObject.createObjectURL(b); - var img = new Image(); - img.onload = function() { - assert(img.complete, 'Image %(filename)s could not be decoded'); - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - preloadedImages['%(filename)s'] = canvas; - URLObject.revokeObjectURL(url); - removeRunDependency(); - }; - img.onerror = function(event) { - console.log('Image %(filename)s could not be decoded'); - }; - img.src = url; -''' % { 'filename': filename } - elif audio: - # Need actual blob constructor here, to set the mimetype or else audios fail to decode - finish = ''' - if (hasBlobConstructor) { - var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' }); - var url = URLObject.createObjectURL(b); // XXX we never revoke this! - var audio = new Audio(); - audio.removedDependency = false; - audio['oncanplaythrough'] = function() { // XXX string for closure - audio['oncanplaythrough'] = null; - preloadedAudios['%(filename)s'] = audio; - if (!audio.removedDependency) { - removeRunDependency(); - audio.removedDependency = true; - } - }; - audio.onerror = function(event) { - if (!audio.removedDependency) { - console.log('Audio %(filename)s could not be decoded or timed out trying to decode'); - removeRunDependency(); - audio.removedDependency = true; - } - }; - setTimeout(audio.onerror, 2000); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang) - audio.src = url; - } else { - preloadedAudios['%(filename)s'] = new Audio(); // empty shim - removeRunDependency(); - } -''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] } - else: - finish = 'removeRunDependency();\n' - - code += ''' - var %(varname)s = new %(request)s(); - %(varname)s.open('GET', '%(netname)s', true); - %(varname)s.responseType = 'arraybuffer'; - %(varname)s.onload = function() { - var arrayBuffer = %(varname)s.response; - assert(arrayBuffer, 'Loading file %(filename)s failed.'); - var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer; - FS.createDataFile('/%(dirname)s', '%(basename)s', byteArray, true, true); - %(finish)s - }; - addRunDependency(); - %(varname)s.send(null); -''' % { - 'request': 'DataRequest', # In the past we also supported XHRs here - 'varname': varname, - 'filename': filename, - 'netname': file_['net_name'], - 'dirname': os.path.dirname(filename), - 'basename': os.path.basename(filename), - 'finish': finish - } - else: - assert 0 - - if final_suffix == 'html': - # Get the big archive and split it up - use_data = '' - for file_ in data_files: - if file_['mode'] == 'preload': - use_data += ''' - curr = DataRequest.prototype.requests['%s']; - curr.response = byteArray.subarray(%d,%d); - curr.onload(); - ''' % (file_['name'], file_['data_start'], file_['data_end']) - use_data += ' removeRunDependency();\n' - - if Compression.on: - use_data = ''' - Module["decompress"](byteArray, function(decompressed) { - byteArray = new Uint8Array(decompressed); - %s - }); - ''' % use_data - - code += ''' - var dataFile = new XMLHttpRequest(); - dataFile.open('GET', '%s', true); - dataFile.responseType = 'arraybuffer'; - dataFile.onload = function() { - var arrayBuffer = dataFile.response; - assert(arrayBuffer, 'Loading data file failed.'); - var byteArray = new Uint8Array(arrayBuffer); - var curr; - %s - }; - addRunDependency(); - dataFile.send(null); - if (Module['setStatus']) Module['setStatus']('Downloading...'); - ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data) - + file_args = [] + if len(preload_files) > 0: + file_args.append('--preload') + file_args += preload_files + if len(embed_files) > 0: + file_args.append('--embed') + file_args += embed_files + if Compression.on: + file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name] + code = execute(shared.ENV_PREFIX + ['python', shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0] src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code) final += '.files.js' open(final, 'w').write(src) @@ -1091,7 +869,7 @@ try: if DEBUG: print >> sys.stderr, 'emcc: applying pre/postjses' src = open(final).read() final += '.pp.js' - open(final, 'w').write((pre_js or '') + src + (post_js or '')) + open(final, 'w').write(pre_js + src + post_js) if DEBUG: save_intermediate('pre-post') # Apply a source code transformation, if requested diff --git a/src/intertyper.js b/src/intertyper.js index 6b91f527..fbad353a 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -794,7 +794,7 @@ function intertyper(data, sidePass, baseLineNums) { value: parseLLVMSegment(typeToken.concat(subSegments[0])) }; return ret; - }); + }).filter(function(param) { return param.value && param.value.ident != 'undef' }); this.forwardItem(item, 'Reintegrator'); } }); diff --git a/src/jsifier.js b/src/jsifier.js index 1d18f292..01ecd7d3 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -71,10 +71,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } } else { - libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free']; - } - if (GENERATING_HTML) { - libFuncsToInclude.push('$Browser'); + libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free', '$Browser']; } libFuncsToInclude.forEach(function(ident) { data.functionStubs.push({ @@ -286,7 +283,7 @@ function JSify(data, functionsOnly, givenFunctions) { var val = LibraryManager.library[shortident]; var padding; if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { - padding = [item.type].concat(zeros(Runtime.getNativeFieldSize(item.type))); + padding = [item.type].concat(zeros(Runtime.getNativeFieldSize(item.type)-1)); } else { padding = makeEmptyStruct(item.type); } diff --git a/src/library.js b/src/library.js index fe6ae0fb..b9c13055 100644 --- a/src/library.js +++ b/src/library.js @@ -28,7 +28,14 @@ LibraryManager.library = { $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + - '__ATEXIT__.push({ func: function() { FS.quit() } });', + '__ATEXIT__.push({ func: function() { FS.quit() } });' + + // export some names through closure + 'Module["FS_createFolder"] = FS.createFolder;' + + 'Module["FS_createPath"] = FS.createPath;' + + 'Module["FS_createDataFile"] = FS.createDataFile;' + + 'Module["FS_createLazyFile"] = FS.createLazyFile;' + + 'Module["FS_createLink"] = FS.createLink;' + + 'Module["FS_createDevice"] = FS.createDevice;', $FS: { // The path to the current folder. currentPath: '/', @@ -1488,7 +1495,7 @@ LibraryManager.library = { lseek: function(fildes, offset, whence) { // off_t lseek(int fildes, off_t offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html - if (FS.streams[fildes] && !FS.streams[fildes].isDevice) { + if (FS.streams[fildes] && !FS.streams[fildes].object.isDevice) { var stream = FS.streams[fildes]; var position = offset; if (whence === 1) { // SEEK_CUR. @@ -2710,24 +2717,19 @@ LibraryManager.library = { }); } else if (next == 's'.charCodeAt(0)) { // String. - var arg = getNextArg('i8*'); - var copiedString; - if (arg) { - copiedString = String_copy(arg); - if (precisionSet && copiedString.length > precision) { - copiedString = copiedString.slice(0, precision); - } - } else { - copiedString = intArrayFromString('(null)', true); - } + var arg = getNextArg('i8*') || 0; // 0 holds '(null)' + var argLength = String_len(arg); + if (precisionSet) argLength = Math.min(String_len(arg), precision); if (!flagLeftAlign) { - while (copiedString.length < width--) { + while (argLength < width--) { ret.push(' '.charCodeAt(0)); } } - ret = ret.concat(copiedString); + for (var i = 0; i < argLength; i++) { + ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}}); + } if (flagLeftAlign) { - while (copiedString.length < width--) { + while (argLength < width--) { ret.push(' '.charCodeAt(0)); } } @@ -4220,9 +4222,17 @@ LibraryManager.library = { }, strpbrk: function(ptr1, ptr2) { - var searchSet = Runtime.set.apply(null, String_copy(ptr2)); - while ({{{ makeGetValue('ptr1', 0, 'i8') }}}) { - if ({{{ makeGetValue('ptr1', 0, 'i8') }}} in searchSet) return ptr1; + var curr; + var searchSet = {}; + while (1) { + var curr = {{{ makeGetValue('ptr2++', 0, 'i8') }}}; + if (!curr) break; + searchSet[curr] = 1; + } + while (1) { + curr = {{{ makeGetValue('ptr1', 0, 'i8') }}}; + if (!curr) break; + if (curr in searchSet) return ptr1; ptr1++; } return 0; @@ -6193,6 +6203,10 @@ LibraryManager.library = { return eval(Pointer_stringify(ptr)); }, + emscripten_random: function() { + return Math.random(); + }, + $Profiling: { max_: 0, times: null, diff --git a/src/library_browser.js b/src/library_browser.js index 59d37336..075bd8bf 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -3,12 +3,25 @@ // Utilities for browser environments mergeInto(LibraryManager.library, { - $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n', // export requestFullScreen + $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n' + // exports + 'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' + + 'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' + + 'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n', $Browser: { mainLoop: { scheduler: null, shouldPause: false, - paused: false + paused: false, + pause: function() { + Browser.mainLoop.shouldPause = true; + }, + resume: function() { + if (Browser.mainLoop.paused) { + Browser.mainLoop.paused = false; + Browser.mainLoop.scheduler(); + } + Browser.mainLoop.shouldPause = false; + }, }, pointerLock: false, moduleContextCreatedCallbacks: [], @@ -86,6 +99,7 @@ mergeInto(LibraryManager.library, { requestFullScreen: function() { var canvas = Module.canvas; function fullScreenChange() { + if (Module['onFullScreen']) Module['onFullScreen'](); if (document['webkitFullScreenElement'] === canvas || document['mozFullScreenElement'] === canvas || document['fullScreenElement'] === canvas) { @@ -128,40 +142,69 @@ mergeInto(LibraryManager.library, { window.requestAnimationFrame(func); }, - getMovementX: function(delta, event) { - if (!Browser.pointerLock) return delta; + getMovementX: function(event) { return event['movementX'] || event['mozMovementX'] || event['webkitMovementX'] || - 0; // delta; + 0; }, - getMovementY: function(delta, event) { - if (!Browser.pointerLock) return delta; + getMovementY: function(event) { return event['movementY'] || event['mozMovementY'] || event['webkitMovementY'] || - 0; // delta; + 0; }, - asyncLoad: function(url, callback) { + xhrLoad: function(url, onload, onerror) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { - var arrayBuffer = xhr.response; + if (xhr.status == 200) { + onload(xhr.response); + } else { + onerror(); + } + }; + xhr.onerror = onerror; + xhr.send(null); + }, + + asyncLoad: function(url, callback) { + Browser.xhrLoad(url, function(arrayBuffer) { assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); callback(new Uint8Array(arrayBuffer)); removeRunDependency(); - }; - xhr.onerror = function(event) { - assert(arrayBuffer, 'Loading data file "' + url + '" failed.'); - }; - xhr.send(null); + }, function(event) { + throw 'Loading data file "' + url + '" failed.'; + }); addRunDependency(); } }, + emscripten_async_wget: function(url, file, onload, onerror) { + url = Pointer_stringify(url); + + Browser.xhrLoad(url, function(response) { + var absolute = Pointer_stringify(file); + var index = absolute.lastIndexOf('/'); + FS.createDataFile( + absolute.substr(0, index), + absolute.substr(index +1), + new Uint8Array(response), + true, true); + + if (onload) { + FUNCTION_TABLE[onload](file); + } + }, function(event) { + if (onerror) { + FUNCTION_TABLE[onerror](file); + } + }); + }, + emscripten_async_run_script__deps: ['emscripten_run_script'], emscripten_async_run_script: function(script, millis) { Module['noExitRuntime'] = true; @@ -210,15 +253,11 @@ mergeInto(LibraryManager.library, { }, emscripten_pause_main_loop: function(func) { - Browser.mainLoop.shouldPause = true; + Browser.mainLoop.pause(); }, emscripten_resume_main_loop: function(func) { - if (Browser.mainLoop.paused) { - Browser.mainLoop.paused = false; - Browser.mainLoop.scheduler(); - } - Browser.mainLoop.shouldPause = false; + Browser.mainLoop.resume(); }, emscripten_async_call: function(func, millis) { diff --git a/src/library_gc.js b/src/library_gc.js index ccf6656d..bf0a6aff 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -16,13 +16,15 @@ if (GC_SUPPORT) { init: function() { assert(!GC.initted); GC.initted = true; -#if GENERATING_HTML - setInterval(function() { - GC.maybeCollect(); - }, 1000); -#else - // No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually + if (ENVIRONMENT_IS_WEB) { + setInterval(function() { + GC.maybeCollect(); + }, 1000); + } else { +#if ASSERTIONS + Module.print('No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually'); #endif + } }, malloc: function(bytes, clear, scannable) { diff --git a/src/library_gl.js b/src/library_gl.js index 9f88260a..fbf5bb12 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4,6 +4,7 @@ */ var LibraryGL = { + $GL__postset: 'GL.init()', $GL: { #if GL_DEBUG debug: true, @@ -21,6 +22,10 @@ var LibraryGL = { packAlignment: 4, // default alignment is 4 bytes unpackAlignment: 4, // default alignment is 4 bytes + init: function() { + Browser.moduleContextCreatedCallbacks.push(GL.initExtensions); + }, + // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters scan: function(table, object) { for (var item in table) { @@ -100,6 +105,19 @@ var LibraryGL = { var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); return (height <= 0) ? 0 : ((height - 1) * alignedRowSize + plainRowSize); + }, + + initExtensions: function() { + if (GL.initExtensions.done) return; + GL.initExtensions.done = true; + + GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); + + GL.anisotropicExt = Module.ctx.getExtension('EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('MOZ_EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); } }, @@ -274,21 +292,23 @@ var LibraryGL = { }, glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, data); + Module.ctx['compressedTexImage2D'](target, level, internalformat, width, height, border, data); }, glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, data); + Module.ctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, data); }, glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { @@ -323,7 +343,11 @@ var LibraryGL = { throw 'Invalid type (' + type + ') passed to glTexImage2D'; } var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + if (type == 0x1401 /* GL_UNSIGNED_BYTE */) { + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + } else { + pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}}; + } } else { pixels = null; } @@ -362,7 +386,11 @@ var LibraryGL = { throw 'Invalid type (' + type + ') passed to glTexSubImage2D'; } var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + if (type == 0x1401 /* GL_UNSIGNED_BYTE */) { + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + } else { + pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}}; + } } else { pixels = null; } @@ -891,28 +919,62 @@ var LibraryGL = { $GLEmulation__postset: 'GLEmulation.init();', $GLEmulation: { + // Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms + fogStart: 0, + fogEnd: 1, + fogDensity: 1.0, + fogColor: null, + fogMode: 0x0800, // GL_EXP + fogEnabled: false, + init: function() { + GLEmulation.fogColor = new Float32Array(4); + // Add some emulation workarounds Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work'); - // XXX some of these ignored capabilities may lead to incorrect rendering, if we do not emulate them in shaders - var ignoredCapabilities = { - 0x0DE1: 1, // GL_TEXTURE_2D - 0x0B20: 1, // GL_LINE_SMOOTH - 0x0B60: 1, // GL_FOG - 0x8513: 1, // GL_TEXTURE_CUBE_MAP - 0x0BA1: 1, // GL_NORMALIZE - 0x0C60: 1, // GL_TEXTURE_GEN_S - 0x0C61: 1 // GL_TEXTURE_GEN_T + // XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders + var validCapabilities = { + 0x0B44: 1, // GL_CULL_FACE + 0x0BE2: 1, // GL_BLEND + 0x0BD0: 1, // GL_DITHER, + 0x0B90: 1, // GL_STENCIL_TEST + 0x0B71: 1, // GL_DEPTH_TEST + 0x0C11: 1, // GL_SCISSOR_TEST + 0x8037: 1, // GL_POLYGON_OFFSET_FILL + 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE + 0x80A0: 1 // GL_SAMPLE_COVERAGE }; _glEnable = function(cap) { - if (cap in ignoredCapabilities) return; + // Clean up the renderer on any change to the rendering state. The optimization of + // skipping renderer setup is aimed at the case of multiple glDraw* right after each other + if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = true; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.enable(cap); }; _glDisable = function(cap) { - if (cap in ignoredCapabilities) return; + if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = false; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.disable(cap); }; + _glIsEnabled = function(cap) { + if (cap == 0x0B60 /* GL_FOG */) { + return GLEmulation.fogEnabled ? 1 : 0; + } else if (!(cap in validCapabilities)) { + return 0; + } + return Module.ctx.isEnabled(cap); + }; var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function(pname, params) { @@ -942,7 +1004,11 @@ var LibraryGL = { _glGetString = function(name_) { switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support - return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements'), 'i8', ALLOC_NORMAL); + return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements' + + (GL.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') + + (GL.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '') + ), 'i8', ALLOC_NORMAL); } return glGetString(name_); }; @@ -986,7 +1052,7 @@ var LibraryGL = { source = source.replace(/gl_ProjectionMatrix/g, 'u_projection'); if (old != source) need_pm = 1; old = source; - source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0], u_modelView[3][0])'); // XXX extremely inefficient + source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient if (old != source) need_mm = 1; old = source; source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView'); @@ -1034,9 +1100,10 @@ var LibraryGL = { source = 'attribute vec3 a_normal; \n' + source.replace(/gl_Normal/g, 'a_normal'); } + // fog if (source.indexOf('gl_FogFragCoord') >= 0) { - source = 'varying float v_fogCoord; \n' + - source.replace(/gl_FogFragCoord/g, 'v_fogCoord'); + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); } } else { // Fragment shader for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { @@ -1049,7 +1116,26 @@ var LibraryGL = { if (source.indexOf('gl_Color') >= 0) { source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color'); } - source = source.replace(/gl_Fog.color/g, 'vec4(0.0)'); // XXX TODO + if (source.indexOf('gl_Fog.color') >= 0) { + source = 'uniform vec4 u_fogColor; \n' + + source.replace(/gl_Fog.color/g, 'u_fogColor'); + } + if (source.indexOf('gl_Fog.end') >= 0) { + source = 'uniform float u_fogEnd; \n' + + source.replace(/gl_Fog.end/g, 'u_fogEnd'); + } + if (source.indexOf('gl_Fog.scale') >= 0) { + source = 'uniform float u_fogScale; \n' + + source.replace(/gl_Fog.scale/g, 'u_fogScale'); + } + if (source.indexOf('gl_Fog.density') >= 0) { + source = 'uniform float u_fogDensity; \n' + + source.replace(/gl_Fog.density/g, 'u_fogDensity'); + } + if (source.indexOf('gl_FogFragCoord') >= 0) { + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); + } source = 'precision mediump float;\n' + source; } #if GL_DEBUG @@ -1105,6 +1191,21 @@ var LibraryGL = { if (program == GL.currProgram) GL.currProgram = 0; }; + // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that. + var zeroUsedPrograms = {}; + var glBindAttribLocation = _glBindAttribLocation; + _glBindAttribLocation = function(program, index, name) { + if (index == 0) zeroUsedPrograms[program] = true; + glBindAttribLocation(program, index, name); + }; + var glLinkProgram = _glLinkProgram; + _glLinkProgram = function(program) { + if (!(program in zeroUsedPrograms)) { + Module.ctx.bindAttribLocation(GL.programs[program], 0, 'a_position'); + } + glLinkProgram(program); + }; + var glBindBuffer = _glBindBuffer; _glBindBuffer = function(target, buffer) { glBindBuffer(target, buffer); @@ -1134,15 +1235,27 @@ var LibraryGL = { } else if (pname == 0x0BA8) { // GL_TEXTURE_MATRIX HEAPF32.set(GL.immediate.matrix['t' + GL.immediate.clientActiveTexture], params >> 2); } else if (pname == 0x0B66) { // GL_FOG_COLOR - {{{ makeSetValue('params', '0', '0', 'float') }}}; + HEAPF32.set(GLEmulation.fogColor, params >> 2); } else if (pname == 0x0B63) { // GL_FOG_START - {{{ makeSetValue('params', '0', '0', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}}; } else if (pname == 0x0B64) { // GL_FOG_END - {{{ makeSetValue('params', '0', '0', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}}; + } else if (pname == 0x0B62) { // GL_FOG_DENSITY + {{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}}; + } else if (pname == 0x0B65) { // GL_FOG_MODE + {{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}}; } else { glGetFloatv(pname, params); } }; + + var glHint = _glHint; + _glHint = function(target, mode) { + if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT + return; + } + glHint(target, mode); + }; }, getProcAddress: function(name) { @@ -1269,12 +1382,16 @@ var LibraryGL = { rendererCache: {}, rendererComponents: {}, // small cache for calls inside glBegin/end. counts how many times the element was seen rendererComponentPointer: 0, // next place to start a glBegin/end component + lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer + lastArrayBuffer: null, // used in conjunction with lastRenderer + lastProgram: null, // "" // The following data structures are used for OpenGL Immediate Mode matrix routines. matrix: {}, matrixStack: {}, currentMatrix: 'm', // default is modelview tempMatrix: null, + matricesModified: false, // Clientside attributes VERTEX: 0, @@ -1324,6 +1441,7 @@ var LibraryGL = { tempBufferIndexLookup: null, tempVertexBuffers: null, tempIndexBuffers: null, + tempQuadIndexBuffer: null, generateTempBuffers: function() { function ceilPower2(x) { @@ -1357,6 +1475,30 @@ var LibraryGL = { last = size; } } + + // GL_QUAD indexes can be precalculated + this.tempQuadIndexBuffer = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer); + var numIndexes = this.MAX_TEMP_BUFFER_SIZE >> 1; + var quadIndexes = new Uint16Array(numIndexes); + var i = 0, v = 0; + while (1) { + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+1; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v+3; + if (i >= numIndexes) break; + v += 4; + } + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, quadIndexes, Module.ctx.STATIC_DRAW); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); }, // Renderers @@ -1394,7 +1536,14 @@ var LibraryGL = { if (!cacheItem[attribute.type]) cacheItem[attribute.type] = {}; cacheItem = cacheItem[attribute.type]; } - if (GL.currProgram) { + if (GLEmulation.fogEnabled) { + var fogParam = GLEmulation.fogMode; + } else { + var fogParam = 0; // all valid modes are non-zero + } + if (!cacheItem[fogParam]) cacheItem[fogParam] = {}; + cacheItem = cacheItem[fogParam]; + if (GL.currProgram) { // Note the order here; this one is last, and optional if (!cacheItem[GL.currProgram]) cacheItem[GL.currProgram] = {}; cacheItem = cacheItem[GL.currProgram]; } @@ -1446,20 +1595,43 @@ var LibraryGL = { } this.program = GL.programs[GL.currProgram]; } else { + // IMPORTANT NOTE: If you parameterize the shader source based on any runtime values + // in order to create the least expensive shader possible based on the features being + // used, you should also update the code in the beginning of getRenderer to make sure + // that you cache the renderer based on the said parameters. this.vertexShader = Module.ctx.createShader(Module.ctx.VERTEX_SHADER); var zero = positionSize == 2 ? '0, ' : ''; + if (GLEmulation.fogEnabled) { + switch (GLEmulation.fogMode) { + case 0x0801: // GL_EXP2 + // fog = exp(-(gl_Fog.density * gl_FogFragCoord)^2) + var fogFormula = ' float fog = exp(-u_fogDensity * u_fogDensity * ecDistance * ecDistance); \n'; + break; + case 0x2601: // GL_LINEAR + // fog = (gl_Fog.end - gl_FogFragCoord) * gl_fog.scale + var fogFormula = ' float fog = (u_fogEnd - ecDistance) * u_fogScale; \n'; + break; + default: // default to GL_EXP + // fog = exp(-gl_Fog.density * gl_FogFragCoord) + var fogFormula = ' float fog = exp(-u_fogDensity * ecDistance); \n'; + break; + } + } Module.ctx.shaderSource(this.vertexShader, 'attribute vec' + positionSize + ' a_position; \n' + 'attribute vec2 a_texCoord0; \n' + (hasTextures ? 'varying vec2 v_texCoord; \n' : '') + 'varying vec4 v_color; \n' + (colorSize ? 'attribute vec4 a_color; \n': 'uniform vec4 u_color; \n') + + (GLEmulation.fogEnabled ? 'varying float v_fogFragCoord; \n' : '') + 'uniform mat4 u_modelView; \n' + 'uniform mat4 u_projection; \n' + 'void main() \n' + '{ \n' + - ' gl_Position = u_projection * (u_modelView * vec4(a_position, ' + zero + '1.0)); \n' + + ' vec4 ecPosition = (u_modelView * vec4(a_position, ' + zero + '1.0)); \n' + // eye-coordinate position + ' gl_Position = u_projection * ecPosition; \n' + (hasTextures ? 'v_texCoord = a_texCoord0; \n' : '') + (colorSize ? 'v_color = a_color; \n' : 'v_color = u_color; \n') + + (GLEmulation.fogEnabled ? 'v_fogFragCoord = abs(ecPosition.z);\n' : '') + '} \n'); Module.ctx.compileShader(this.vertexShader); @@ -1468,16 +1640,30 @@ var LibraryGL = { 'varying vec2 v_texCoord; \n' + 'uniform sampler2D u_texture; \n' + 'varying vec4 v_color; \n' + + (GLEmulation.fogEnabled ? ( + 'varying float v_fogFragCoord; \n' + + 'uniform vec4 u_fogColor; \n' + + 'uniform float u_fogEnd; \n' + + 'uniform float u_fogScale; \n' + + 'uniform float u_fogDensity; \n' + + 'float ffog(in float ecDistance) { \n' + + fogFormula + + ' fog = clamp(fog, 0.0, 1.0); \n' + + ' return fog; \n' + + '} \n' + ) : '') + 'void main() \n' + '{ \n' + (hasTextures ? 'gl_FragColor = v_color * texture2D( u_texture, v_texCoord );\n' : 'gl_FragColor = v_color;\n') + + (GLEmulation.fogEnabled ? 'gl_FragColor = vec4(mix(u_fogColor.rgb, gl_FragColor.rgb, ffog(v_fogFragCoord)), gl_FragColor.a); \n' : '') + '} \n'); Module.ctx.compileShader(this.fragmentShader); this.program = Module.ctx.createProgram(); Module.ctx.attachShader(this.program, this.vertexShader); Module.ctx.attachShader(this.program, this.fragmentShader); + Module.ctx.bindAttribLocation(this.program, 0, 'a_position'); Module.ctx.linkProgram(this.program); } @@ -1507,9 +1693,55 @@ var LibraryGL = { this.hasNormal = normalSize > 0 && this.normalLocation >= 0; this.floatType = Module.ctx.FLOAT; // minor optimization + + this.fogColorLocation = Module.ctx.getUniformLocation(this.program, 'u_fogColor'); + this.fogEndLocation = Module.ctx.getUniformLocation(this.program, 'u_fogEnd'); + this.fogScaleLocation = Module.ctx.getUniformLocation(this.program, 'u_fogScale'); + this.fogDensityLocation = Module.ctx.getUniformLocation(this.program, 'u_fogDensity'); + this.hasFog = !!(this.fogColorLocation || this.fogEndLocation || + this.fogScaleLocation || this.fogDensityLocation); }, prepare: function() { + // Calculate the array buffer + var arrayBuffer; + if (!GL.currArrayBuffer) { + var start = GL.immediate.firstVertex*GL.immediate.stride; + var end = GL.immediate.lastVertex*GL.immediate.stride; + assert(end <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too much vertex data'); + arrayBuffer = GL.immediate.tempVertexBuffers[GL.immediate.tempBufferIndexLookup[end]]; + // TODO: consider using the last buffer we bound, if it was larger. downside is larger buffer, but we might avoid rebinding and preparing + } else { + arrayBuffer = GL.currArrayBuffer; + } + + // If the array buffer is unchanged and the renderer as well, then we can avoid all the work here + // XXX We use some heuristics here, and this may not work in all cases. Try disabling this if you + // have odd glitches (by setting canSkip always to 0, or even cleaning up the renderer right + // after rendering) + var lastRenderer = GL.immediate.lastRenderer; + var canSkip = this == lastRenderer && + arrayBuffer == GL.immediate.lastArrayBuffer && + (GL.currProgram || this.program) == GL.immediate.lastProgram && + !GL.immediate.matricesModified; + if (!canSkip && lastRenderer) lastRenderer.cleanup(); + if (!GL.currArrayBuffer) { + // Bind the array buffer and upload data after cleaning up the previous renderer + if (arrayBuffer != GL.immediate.lastArrayBuffer) { + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, arrayBuffer); + } + Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, GL.immediate.vertexData.subarray(start >> 2, end >> 2)); + } + if (canSkip) return; + GL.immediate.lastRenderer = this; + GL.immediate.lastArrayBuffer = arrayBuffer; + GL.immediate.lastProgram = GL.currProgram || this.program; + GL.immediate.matricesModified = false; + + if (!GL.currProgram) { + Module.ctx.useProgram(this.program); + } + if (this.modelViewLocation) Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']); if (this.projectionLocation) Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']); @@ -1550,6 +1782,12 @@ var LibraryGL = { Module.ctx.bindTexture(Module.ctx.TEXTURE_2D, texture); Module.ctx.uniform1i(this.textureLocation, 0); } + if (this.hasFog) { + if (this.fogColorLocation) Module.ctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor); + if (this.fogEndLocation) Module.ctx.uniform1f(this.fogEndLocation, GLEmulation.fogEnd); + if (this.fogScaleLocation) Module.ctx.uniform1f(this.fogScaleLocation, 1/(GLEmulation.fogEnd - GLEmulation.fogStart)); + if (this.fogDensityLocation) Module.ctx.uniform1f(this.fogDensityLocation, GLEmulation.fogDensity); + } }, cleanup: function() { @@ -1567,6 +1805,17 @@ var LibraryGL = { if (this.hasNormal) { Module.ctx.disableVertexAttribArray(this.normalLocation); } + if (!GL.currProgram) { + Module.ctx.useProgram(null); + } + if (!GL.currArrayBuffer) { + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null); + } + + GL.immediate.lastRenderer = null; + GL.immediate.lastArrayBuffer = null; + GL.immediate.lastProgram = null; + GL.immediate.matricesModified = true; } }; ret.init(); @@ -1601,7 +1850,9 @@ var LibraryGL = { // Initialize matrix library GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['m']); GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['p']); for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create(); } @@ -1748,43 +1999,19 @@ var LibraryGL = { emulatedElementArrayBuffer = true; } } else if (GL.immediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes - if (GL.immediate.mode == 7) { // GL_QUADS - var numQuads = numVertexes / 4; - assert(numQuads % 1 == 0); - for (var i = 0; i < numQuads; i++) { - var start = i*4; - GL.immediate.indexData[numIndexes++] = start; - GL.immediate.indexData[numIndexes++] = start+1; - GL.immediate.indexData[numIndexes++] = start+2; - GL.immediate.indexData[numIndexes++] = start; - GL.immediate.indexData[numIndexes++] = start+2; - GL.immediate.indexData[numIndexes++] = start+3; - } - } else { - throw 'unsupported immediate mode ' + GL.immediate.mode; - } - assert(numIndexes << 1 <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)'); - var indexBuffer = GL.immediate.tempIndexBuffers[GL.immediate.tempBufferIndexLookup[numIndexes << 1]]; - Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, indexBuffer); - Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, 0, this.indexData.subarray(0, numIndexes)); + if (GL.immediate.mode != 7) throw 'unsupported immediate mode ' + GL.immediate.mode; // GL_QUADS + // GL.immediate.firstVertex is the first vertex we want. Quad indexes are in the pattern + // 0 1 2, 0 2 3, 4 5 6, 4 6 7, so we need to look at index firstVertex * 1.5 to see it. + // Then since indexes are 2 bytes each, that means 3 + assert(GL.immediate.firstVertex % 4 == 0); + ptr = GL.immediate.firstVertex*3; + var numQuads = numVertexes / 4; + numIndexes = numQuads * 6; // 0 1 2, 0 2 3 pattern + assert(ptr + (numIndexes << 1) <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)'); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer); emulatedElementArrayBuffer = true; } - if (!GL.currArrayBuffer) { - Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.vertexObject); - var start = GL.immediate.firstVertex*GL.immediate.stride; - var end = GL.immediate.lastVertex*GL.immediate.stride; - assert(end <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too much vertex data'); - var vertexBuffer = GL.immediate.tempVertexBuffers[GL.immediate.tempBufferIndexLookup[end]]; - Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, vertexBuffer); - Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, this.vertexData.subarray(start >> 2, end >> 2)); - } - - // Render - if (!GL.currProgram) { - Module.ctx.useProgram(renderer.program); - } - renderer.prepare(); if (numIndexes) { @@ -1793,17 +2020,9 @@ var LibraryGL = { Module.ctx.drawArrays(GL.immediate.mode, startIndex, numVertexes); } - renderer.cleanup(); - - if (!GL.currArrayBuffer) { - Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null); - } if (emulatedElementArrayBuffer) { Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null); } - if (!GL.currProgram) { - Module.ctx.useProgram(null); - } } }, @@ -1886,7 +2105,7 @@ var LibraryGL = { }, glColor4us__deps: ['glColor4f'], glColor4us: function(r, g, b, a) { - _glColor4f((r&65525)/65535, (g&65525)/65535, (b&65525)/65535, (a&65525)/65535); + _glColor4f((r&65535)/65535, (g&65535)/65535, (b&65535)/65535, (a&65535)/65535); }, glColor4ui__deps: ['glColor4f'], glColor4ui: function(r, g, b, a) { @@ -1931,10 +2150,58 @@ var LibraryGL = { _glColor4f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, {{{ makeGetValue('p', '8', 'float') }}}, {{{ makeGetValue('p', '12', 'float') }}}); }, - glFogf: function(){}, // TODO - glFogi: function(){}, // TODO - glFogx: function(){}, // TODO - glFogfv: function(){}, // TODO + glFogf: function(pname, param) { // partial support, TODO + switch(pname) { + case 0x0B63: // GL_FOG_START + GLEmulation.fogStart = param; break; + case 0x0B64: // GL_FOG_END + GLEmulation.fogEnd = param; break; + case 0x0B62: // GL_FOG_DENSITY + GLEmulation.fogDensity = param; break; + case 0x0B65: // GL_FOG_MODE + switch (param) { + case 0x0801: // GL_EXP2 + case 0x2601: // GL_LINEAR + GLEmulation.fogMode = param; break; + default: // default to GL_EXP + GLEmulation.fogMode = 0x0800 /* GL_EXP */; break; + } + break; + } + }, + glFogi__deps: ['glFogf'], + glFogi: function(pname, param) { + return _glFogf(pname, param); + }, + glFogfv__deps: ['glFogf'], + glFogfv: function(pname, param) { // partial support, TODO + switch(pname) { + case 0x0B66: // GL_FOG_COLOR + GLEmulation.fogColor[0] = {{{ makeGetValue('param', '0', 'float') }}}; + GLEmulation.fogColor[1] = {{{ makeGetValue('param', '4', 'float') }}}; + GLEmulation.fogColor[2] = {{{ makeGetValue('param', '8', 'float') }}}; + GLEmulation.fogColor[3] = {{{ makeGetValue('param', '12', 'float') }}}; + break; + case 0x0B63: // GL_FOG_START + case 0x0B64: // GL_FOG_END + _glFogf(pname, {{{ makeGetValue('param', '0', 'float') }}}); break; + } + }, + glFogiv__deps: ['glFogf'], + glFogiv: function(pname, param) { + switch(pname) { + case 0x0B66: // GL_FOG_COLOR + GLEmulation.fogColor[0] = ({{{ makeGetValue('param', '0', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[1] = ({{{ makeGetValue('param', '4', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[2] = ({{{ makeGetValue('param', '8', 'i32') }}}/2147483647)/2.0+0.5; + GLEmulation.fogColor[3] = ({{{ makeGetValue('param', '12', 'i32') }}}/2147483647)/2.0+0.5; + break; + default: + _glFogf(pname, {{{ makeGetValue('param', '0', 'i32') }}}); break; + } + }, + glFogx: 'glFogi', + glFogxv: 'glFogiv', glPolygonMode: function(){}, // TODO @@ -2011,20 +2278,24 @@ var LibraryGL = { }, glPushMatrix: function() { + GL.immediate.matricesModified = true; GL.immediate.matrixStack[GL.immediate.currentMatrix].push( Array.prototype.slice.call(GL.immediate.matrix[GL.immediate.currentMatrix])); }, glPopMatrix: function() { + GL.immediate.matricesModified = true; GL.immediate.matrix[GL.immediate.currentMatrix] = GL.immediate.matrixStack[GL.immediate.currentMatrix].pop(); }, glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'], glLoadIdentity: function() { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadMatrixd: function(matrix) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); }, @@ -2032,30 +2303,36 @@ var LibraryGL = { #if GL_DEBUG if (GL.debug) Module.printErr('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16))); #endif + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadTransposeMatrixd: function(matrix) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); GL.immediate.matrix.lib.mat4.transpose(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glLoadTransposeMatrixf: function(matrix) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}, GL.immediate.matrix[GL.immediate.currentMatrix]); GL.immediate.matrix.lib.mat4.transpose(GL.immediate.matrix[GL.immediate.currentMatrix]); }, glMultMatrixd: function(matrix) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], {{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}); }, glMultMatrixf: function(matrix) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], {{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}); }, glMultTransposeMatrixd: function(matrix) { + GL.immediate.matricesModified = true; var colMajor = GL.immediate.matrix.lib.mat4.create(); GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+16*8') }}}, colMajor); GL.immediate.matrix.lib.mat4.transpose(colMajor); @@ -2063,6 +2340,7 @@ var LibraryGL = { }, glMultTransposeMatrixf: function(matrix) { + GL.immediate.matricesModified = true; var colMajor = GL.immediate.matrix.lib.mat4.create(); GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}, colMajor); GL.immediate.matrix.lib.mat4.transpose(colMajor); @@ -2070,27 +2348,32 @@ var LibraryGL = { }, glFrustum: function(left, right, bottom, top_, nearVal, farVal) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal)); }, glOrtho: function(left, right, bottom, top_, nearVal, farVal) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.ortho(left, right, bottom, top_, nearVal, farVal)); }, glOrthof: 'glOrtho', glScaled: function(x, y, z) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.scale(GL.immediate.matrix[GL.immediate.currentMatrix], [x, y, z]); }, glScalef: 'glScaled', glTranslated: function(x, y, z) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.translate(GL.immediate.matrix[GL.immediate.currentMatrix], [x, y, z]); }, glTranslatef: 'glTranslated', glRotated: function(angle, x, y, z) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.rotate(GL.immediate.matrix[GL.immediate.currentMatrix], angle*Math.PI/180, [x, y, z]); }, glRotatef: 'glRotated', @@ -2098,11 +2381,13 @@ var LibraryGL = { // GLU gluPerspective: function(fov, aspect, near, far) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix], GL.immediate.matrix.lib.mat4.perspective(fov, aspect, near, far, GL.immediate.currentMatrix)); }, gluLookAt: function(ex, ey, ez, cx, cy, cz, ux, uy, uz) { + GL.immediate.matricesModified = true; GL.immediate.matrix.lib.mat4.lookAt(GL.immediate.matrix[GL.immediate.currentMatrix], [ex, ey, ez], [cx, cy, cz], [ux, uy, uz]); }, @@ -2157,8 +2442,8 @@ var LibraryGL = { // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name [[0, 'shadeModel getError* finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset sampleCoverage isEnabled*'], - [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], + [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'], [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'], [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], diff --git a/src/library_sdl.js b/src/library_sdl.js index 33a33c09..88649c38 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -16,9 +16,18 @@ var LibrarySDL = { surfaces: {}, events: [], - audios: [null], fonts: [null], + audios: [null], + music: { + audio: null, + volume: 1.0 + }, + mixerFrequency: 22050, + mixerFormat: 0x8010, // AUDIO_S16LSB + mixerNumChannels: 2, + mixerChunkSize: 1024, + keyboardState: null, shiftKey: false, ctrlKey: false, @@ -218,6 +227,10 @@ var LibrarySDL = { return 'rgba(' + r + ',' + g + ',' + b + ',' + (a/255) + ')'; }, + translateRGBAToColor: function(r, g, b, a) { + return (r << 24) + (g << 16) + (b << 8) + a; + }, + makeSurface: function(width, height, flags, usePageCanvas, source, rmask, gmask, bmask, amask) { flags = flags || 0; var surf = _malloc(14*Runtime.QUANTUM_SIZE); // SDL_Surface has 14 fields of quantum size @@ -225,19 +238,23 @@ var LibrarySDL = { var pixelFormat = _malloc(18*Runtime.QUANTUM_SIZE); flags |= 1; // SDL_HWSURFACE - this tells SDL_MUSTLOCK that this needs to be locked + //surface with SDL_HWPALETTE flag is 8bpp surface (1 byte) + var is_SDL_HWPALETTE = flags & 0x00200000; + var bpp = is_SDL_HWPALETTE ? 1 : 4; + {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*0', '0', 'flags', 'i32') }}} // SDL_Surface.flags {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*1', '0', 'pixelFormat', 'void*') }}} // SDL_Surface.format TODO {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*2', '0', 'width', 'i32') }}} // SDL_Surface.w {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*3', '0', 'height', 'i32') }}} // SDL_Surface.h - {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width*4', 'i32') }}} // SDL_Surface.pitch, assuming RGBA for now, + {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*4', '0', 'width * bpp', 'i32') }}} // SDL_Surface.pitch, assuming RGBA or indexed for now, // since that is what ImageData gives us in browsers {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*5', '0', 'buffer', 'void*') }}} // SDL_Surface.pixels {{{ makeSetValue('surf+Runtime.QUANTUM_SIZE*6', '0', '0', 'i32*') }}} // SDL_Surface.offset {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.format', '0', '-2042224636', 'i32') }}} // SDL_PIXELFORMAT_RGBA8888 {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.palette', '0', '0', 'i32') }}} // TODO - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', '32', 'i8') }}} // TODO - {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', '4', 'i8') }}} // TODO + {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BitsPerPixel', '0', 'bpp * 8', 'i8') }}} + {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.BytesPerPixel', '0', 'bpp', 'i8') }}} {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Rmask', '0', 'rmask || 0x000000ff', 'i32') }}} {{{ makeSetValue('pixelFormat + SDL.structs.PixelFormat.Gmask', '0', 'gmask || 0x0000ff00', 'i32') }}} @@ -267,11 +284,54 @@ var LibrarySDL = { flags: flags, locked: 0, usePageCanvas: usePageCanvas, - source: source + source: source, + + isFlagSet: function (flag) { + return flags & flag; + } }; + return surf; }, + // Copy data from the C++-accessible storage to the canvas backing + // for surface with HWPALETTE flag(8bpp depth) + copyIndexedColorData: function(surfData, rX, rY, rW, rH) { + // HWPALETTE works with palette + // setted by SDL_SetColors + if (!surfData.colors) { + return; + } + + var fullWidth = Module['canvas'].width; + var fullHeight = Module['canvas'].height; + + var startX = rX || 0; + var startY = rY || 0; + var endX = (rW || (fullWidth - startX)) + startX; + var endY = (rH || (fullHeight - startY)) + startY; + + var buffer = surfData.buffer; + var data = surfData.image.data; + var colors = surfData.colors; + + for (var y = startY; y < endY; ++y) { + var indexBase = y * fullWidth; + var colorBase = indexBase * 4; + for (var x = startX; x < endX; ++x) { + // HWPALETTE have only 256 colors (not rgba) + var index = {{{ makeGetValue('buffer + indexBase + x', '0', 'i8', null, true) }}}; + var color = colors[index] || [Math.floor(Math.random()*255),Math.floor(Math.random()*255),Math.floor(Math.random()*255)]; // XXX + var colorOffset = colorBase + x * 4; + + data[colorOffset ] = color[0]; + data[colorOffset +1] = color[1]; + data[colorOffset +2] = color[2]; + //unused: data[colorOffset +3] = color[3]; + } + } + }, + freeSurface: function(surf) { _free(SDL.surfaces[surf].buffer); _free(SDL.surfaces[surf].pixelFormat); @@ -383,8 +443,21 @@ var LibrarySDL = { } // fall through case 'mousemove': { - var x = event.pageX - Module['canvas'].offsetLeft; - var y = event.pageY - Module['canvas'].offsetTop; + if (Browser.pointerLock) { + // When the pointer is locked, calculate the coordinates + // based on the movement of the mouse. + var movementX = Browser.getMovementX(event); + var movementY = Browser.getMovementY(event); + var x = SDL.mouseX + movementX; + var y = SDL.mouseY + movementY; + } else { + // Otherwise, calculate the movement based on the changes + // in the coordinates. + var x = event.pageX - Module["canvas"].offsetLeft; + var y = event.pageY - Module["canvas"].offsetTop; + var movementX = x - SDL.mouseX; + var movementY = y - SDL.mouseY; + } if (event.type != 'mousemove') { var down = event.type === 'mousedown'; {{{ makeSetValue('ptr', 'SDL.structs.MouseButtonEvent.type', 'SDL.DOMEventToSDLEvent[event.type]', 'i32') }}}; @@ -397,8 +470,8 @@ var LibrarySDL = { {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.state', 'SDL.buttonState', 'i8') }}}; {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.x', 'x', 'i32') }}}; {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.y', 'y', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'Browser.getMovementX(x - SDL.mouseX, event)', 'i32') }}}; - {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'Browser.getMovementY(y - SDL.mouseY, event)', 'i32') }}}; + {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.xrel', 'movementX', 'i32') }}}; + {{{ makeSetValue('ptr', 'SDL.structs.MouseMotionEvent.yrel', 'movementY', 'i32') }}}; } SDL.mouseX = x; SDL.mouseY = y; @@ -434,6 +507,16 @@ var LibrarySDL = { } }, + setGetVolume: function(info, volume) { + if (!info) return 0; + var ret = info.volume * 128; // MIX_MAX_VOLUME + if (volume != -1) { + info.volume = volume / 128; + if (info.audio) info.audio.volume = info.volume; + } + return ret; + }, + // Debugging debugSurface: function(surfData) { @@ -536,6 +619,10 @@ var LibrarySDL = { return SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen'); }, + SDL_GetVideoSurface: function() { + return SDL.screen; + }, + SDL_QuitSubSystem: function(flags) { Module.print('SDL_QuitSubSystem called (and ignored)'); }, @@ -566,6 +653,26 @@ var LibrarySDL = { } if (SDL.defaults.copyOnLock) { // Copy pixel data to somewhere accessible to 'C/C++' + if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) { + // If this is neaded then + // we should compact the data from 32bpp to 8bpp index. + // I think best way to implement this is use + // additional colorMap hash (color->index). + // Something like this: + // + // var size = surfData.width * surfData.height; + // var data = ''; + // for (var i = 0; i<size; i++) { + // var color = SDL.translateRGBAToColor( + // surfData.image.data[i*4 ], + // surfData.image.data[i*4 +1], + // surfData.image.data[i*4 +2], + // 255); + // var index = surfData.colorMap[color]; + // {{{ makeSetValue('surfData.buffer', 'i', 'index', 'i8') }}}; + // } + throw 'CopyOnLock is not supported for SDL_LockSurface with SDL_HWPALETTE flag set' + new Error().stack; + } else { #if USE_TYPED_ARRAYS == 2 HEAPU8.set(surfData.image.data, surfData.buffer); #else @@ -574,7 +681,9 @@ var LibrarySDL = { {{{ makeSetValue('surfData.buffer', 'i', 'surfData.image.data[i]', 'i8') }}}; } #endif + } } + // Mark in C/C++-accessible SDL structure // SDL_Surface has the following fields: Uint32 flags, SDL_PixelFormat *format; int w, h; Uint16 pitch; void *pixels; ... // So we have fields all of the same size, and 5 of them before us. @@ -592,8 +701,10 @@ var LibrarySDL = { if (surfData.locked > 0) return; // Copy pixel data to image - var num = surfData.image.data.length; - if (!surfData.colors) { + if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) { + SDL.copyIndexedColorData(surfData); + } else if (!surfData.colors) { + var num = surfData.image.data.length; var data = surfData.image.data; var buffer = surfData.buffer; #if USE_TYPED_ARRAYS == 2 @@ -615,7 +726,7 @@ var LibrarySDL = { for (var i = 0; i < num; i++) { // We may need to correct signs here. Potentially you can hardcode a write of 255 to alpha, say, and // the compiler may decide to write -1 in the llvm bitcode... - data[i] = {{{ makeGetValue('buffer', 'i', 'i8') + (CORRECT_SIGNS ? '&0xff' : '') }}}; + data[i] = {{{ makeGetValue('buffer', 'i', 'i8', null, true) }}}; if (i % 4 == 3) data[i] = 0xff; } #endif @@ -629,7 +740,7 @@ var LibrarySDL = { var base = y*width*4; for (var x = 0; x < width; x++) { // See comment above about signs - var val = {{{ makeGetValue('s++', '0', 'i8') + (CORRECT_SIGNS ? '&0xff' : '') }}}; + var val = {{{ makeGetValue('s++', '0', 'i8', null, true) }}}; var color = colors[val] || [Math.floor(Math.random()*255),Math.floor(Math.random()*255),Math.floor(Math.random()*255)]; // XXX var start = base + x*4; data[start] = color[0]; @@ -753,7 +864,16 @@ var LibrarySDL = { SDL_FillRect: function(surf, rect, color) { var surfData = SDL.surfaces[surf]; assert(!surfData.locked); // but we could unlock and re-lock if we must.. - var r = SDL.loadRect(rect); + + if (surfData.isFlagSet(0x00200000 /* SDL_HWPALETTE */)) { + //in SDL_HWPALETTE color is index (0..255) + //so we should translate 1 byte value to + //32 bit canvas + color = surfData.colors[color] || [0, 0, 0, 255]; + color = SDL.translateRGBAToColor(color[0], color[1], color[2], 255); + } + + var r = rect ? SDL.loadRect(rect) : { x: 0, y: 0, w: surfData.width, h: surfData.height }; surfData.ctx.save(); surfData.ctx.fillStyle = SDL.translateColorToCSSRGBA(color); surfData.ctx.fillRect(r.x, r.y, r.w, r.h); @@ -809,10 +929,24 @@ var LibrarySDL = { SDL_SetColors: function(surf, colors, firstColor, nColors) { var surfData = SDL.surfaces[surf]; - surfData.colors = []; - for (var i = firstColor; i < nColors; i++) { - surfData.colors[i] = Array_copy(colors + i*4, colors + i*4 + 4); + + // we should create colors array + // only once cause client code + // often wants to change portion + // of palette not all palette. + if (!surfData.colors) { + surfData.colors = []; + } + + for (var i = firstColor; i < firstColor + nColors; i++) { + surfData.colors[i] = [ + {{{ makeGetValue('colors', 'i*4', 'i8', null, true) }}}, + {{{ makeGetValue('colors', 'i*4 + 1', 'i8', null, true) }}}, + {{{ makeGetValue('colors', 'i*4 + 2', 'i8', null, true) }}}, + {{{ makeGetValue('colors', 'i*4 + 3', 'i8', null, true) }}} + ]; } + return 1; }, @@ -836,7 +970,7 @@ var LibrarySDL = { // Convert the path to relative filename = filename.substr(1); } - var raw = preloadedImages[filename]; + var raw = Module["preloadedImages"][filename]; if (!raw) { Runtime.warnOnce('Cannot find preloaded image ' + filename); return 0; @@ -928,10 +1062,17 @@ var LibrarySDL = { SDL_CondWait: function() {}, SDL_DestroyCond: function() {}, + SDL_StartTextInput: function() {}, // TODO + SDL_StopTextInput: function() {}, // TODO + // SDL Mixer Mix_OpenAudio: function(frequency, format, channels, chunksize) { SDL.allocateChannels(32); + SDL.mixerFrequency = frequency; + SDL.mixerFormat = format; + SDL.mixerNumChannels = channels; + SDL.mixerChunkSize = chunksize; return 0; }, @@ -947,11 +1088,13 @@ var LibrarySDL = { }, Mix_Volume: function(channel, volume) { - var info = SDL.channels[channel]; - var ret = info.volume * 128; - info.volume = volume / 128; - if (info.audio) info.audio.volume = info.volume; - return ret; + if (channel == -1) { + for (var i = 0; i < SDL.numChannels-1; i++) { + _Mix_Volume(i, volume); + } + return _Mix_Volume(SDL.numChannels-1, volume); + } + return SDL.setGetVolume(SDL.channels[channel], volume); }, Mix_SetPanning: function() { @@ -960,7 +1103,7 @@ var LibrarySDL = { Mix_LoadWAV_RW: function(filename, freesrc) { filename = FS.standardizePath(Pointer_stringify(filename)); - var raw = preloadedAudios[filename]; + var raw = Module["preloadedAudios"][filename]; if (!raw) { Runtime.warnOnce('Cannot find preloaded audio ' + filename); return 0; @@ -973,13 +1116,32 @@ var LibrarySDL = { return id; }, + Mix_QuickLoad_RAW: function(mem, len) { + var audio = new Audio(); + audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency); + var numSamples = (len / (SDL.mixerNumChannels * 2)) | 0; + var buffer = new Float32Array(numSamples); + for (var i = 0; i < numSamples; ++i) { + buffer[i] = ({{{ makeGetValue('mem', 'i*2', 'i16', 0, 0) }}}) / 0x8000; // hardcoded 16-bit audio, signed (TODO: reSign if not ta2?) + } + var id = SDL.audios.length; + SDL.audios.push({ + source: '', + audio: audio, + buffer: buffer + }); + return id; + }, + Mix_FreeChunk: function(id) { SDL.audios[id] = null; }, Mix_PlayChannel: function(channel, id, loops) { // TODO: handle loops - var audio = SDL.audios[id].audio; + var info = SDL.audios[id]; + if (!info) return 0; + var audio = info.audio; if (!audio) return 0; if (channel == -1) { channel = 0; @@ -990,15 +1152,20 @@ var LibrarySDL = { } } } - var info = SDL.channels[channel]; - info.audio = audio.cloneNode(true); + var channelInfo = SDL.channels[channel]; + channelInfo.audio = audio = audio.cloneNode(true); if (SDL.channelFinished) { - info.audio['onended'] = function() { // TODO: cache these + audio['onended'] = function() { // TODO: cache these Runtime.getFuncWrapper(SDL.channelFinished)(channel); } } - info.audio.play(); - info.audio.volume = info.volume; + if (info.buffer) { + audio['mozSetup'](SDL.mixerNumChannels, SDL.mixerFrequency); + audio["mozWriteAudio"](info.buffer); + } else { + audio.play(); + } + audio.volume = channelInfo.volume; return channel; }, Mix_PlayChannelTimed: 'Mix_PlayChannel', // XXX ignore Timing @@ -1019,48 +1186,59 @@ var LibrarySDL = { return 0; }, + Mix_HookMusicFinished__deps: ['Mix_HaltMusic'], Mix_HookMusicFinished: function(func) { SDL.hookMusicFinished = func; + if (SDL.music.audio) { // ensure the callback will be called, if a music is already playing + SDL.music.audio['onended'] = _Mix_HaltMusic; + } }, - Mix_VolumeMusic: function(func) { - return 0; // TODO + Mix_VolumeMusic: function(volume) { + return SDL.setGetVolume(SDL.music, volume); }, Mix_LoadMUS: 'Mix_LoadWAV_RW', Mix_FreeMusic: 'Mix_FreeChunk', + Mix_PlayMusic__deps: ['Mix_HaltMusic'], Mix_PlayMusic: function(id, loops) { loops = Math.max(loops, 1); var audio = SDL.audios[id].audio; if (!audio) return 0; audio.loop = loops != 1; // TODO: handle N loops for finite N - audio.play(); - SDL.music = audio; + if (SDL.audios[id].buffer) { + audio["mozWriteAudio"](SDL.audios[id].buffer); + } else { + audio.play(); + } + audio.volume = SDL.music.volume; + audio['onended'] = _Mix_HaltMusic; // will send callback + SDL.music.audio = audio; return 0; }, - Mix_PauseMusic: function(id) { - var audio = SDL.audios[id]; + Mix_PauseMusic: function() { + var audio = SDL.music.audio; if (!audio) return 0; - audio.audio.pause(); + audio.pause(); return 0; }, - Mix_ResumeMusic: function(id) { - var audio = SDL.audios[id]; + Mix_ResumeMusic: function() { + var audio = SDL.music.audio; if (!audio) return 0; - audio.audio.play(); + audio.play(); return 0; }, Mix_HaltMusic: function() { - var audio = SDL.music; + var audio = SDL.music.audio; if (!audio) return 0; audio.src = audio.src; // rewind audio.pause(); - SDL.music = null; + SDL.music.audio = null; if (SDL.hookMusicFinished) { FUNCTION_TABLE[SDL.hookMusicFinished](); } @@ -1071,6 +1249,14 @@ var LibrarySDL = { Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect + Mix_PlayingMusic: function() { + return (SDL.music.audio && !SDL.music.audio.paused) ? 1 : 0; + }, + + Mix_PausedMusic: function() { + return (SDL.music.audio && SDL.music.audio.paused) ? 1 : 0; + }, + // SDL TTF TTF_Init: function() { return 0 }, diff --git a/src/parseTools.js b/src/parseTools.js index e13d8807..4f2f7142 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -775,7 +775,7 @@ function generateStructTypes(type) { if (USE_TYPED_ARRAYS == 2 && type == 'i64') { return ['i64', 0, 0, 0, 'i32', 0, 0, 0]; } - return [type].concat(zeros(Runtime.getNativeFieldSize(type))); + return [type].concat(zeros(Runtime.getNativeFieldSize(type)-1)); } // Avoid multiple concats by finding the size first. This is much faster @@ -1264,7 +1264,7 @@ function makePointer(slab, pos, allocator, type) { var evaled = typeof slab === 'string' ? eval(slab) : slab; de = dedup(evaled); if (de.length === 1 && de[0] === 0) { - slab = evaled.length; + slab = types.length; } // TODO: if not all zeros, at least filter out items with type === 0. requires cleverness to know how to skip at runtime though. also // be careful of structure padding diff --git a/src/postamble.js b/src/postamble.js index ea03391c..10ac1888 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -33,11 +33,13 @@ function run(args) { args = args || Module['arguments']; if (Module['preRun']) { - Module['preRun'](); - if (runDependencies > 0) { - // preRun added a dependency, run will be called later - Module['preRun'] = null; - return 0; + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length > 0) { + Module['preRun'].pop()(); + if (runDependencies > 0) { + // preRun added a dependency, run will be called later + return 0; + } } } @@ -51,12 +53,14 @@ function run(args) { } } if (Module['postRun']) { - Module['postRun'](); + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length > 0) { + Module['postRun'].pop()(); + } } return ret; } -#if GENERATING_HTML if (Module['setStatus']) { Module['setStatus']('Running...'); setTimeout(function() { @@ -69,9 +73,6 @@ function run(args) { } else { return doRun(); } -#else - return doRun(); -#endif } Module['run'] = run; diff --git a/src/preamble.js b/src/preamble.js index 997e5ab3..ae00b796 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -326,6 +326,7 @@ function ccall(ident, returnType, argTypes, args) { var stack = 0; function toC(value, type) { if (type == 'string') { + if (value === null || value === undefined || value === 0) return 0; // null string if (!stack) stack = Runtime.stackSave(); var ret = Runtime.stackAlloc(value.length+1); writeStringToMemory(value, ret); @@ -721,40 +722,6 @@ function exitRuntime() { CorrectionsMonitor.print(); } - -// Copies a list of num items on the HEAP into a -// a normal JavaScript array of numbers -function Array_copy(ptr, num) { -#if USE_TYPED_ARRAYS == 1 - // TODO: In the SAFE_HEAP case, do some reading here, for debugging purposes - currently this is an 'unnoticed read'. - return Array.prototype.slice.call(IHEAP.subarray(ptr, ptr+num)); // Make a normal array out of the typed 'view' - // Consider making a typed array here, for speed? -#endif -#if USE_TYPED_ARRAYS == 2 - return Array.prototype.slice.call(HEAP8.subarray(ptr, ptr+num)); // Make a normal array out of the typed 'view' - // Consider making a typed array here, for speed? -#endif - return HEAP.slice(ptr, ptr+num); -} -Module['Array_copy'] = Array_copy; - -#if USE_TYPED_ARRAYS -// Copies a list of num items on the HEAP into a -// JavaScript typed array. -function TypedArray_copy(ptr, num, offset /*optional*/) { - // TODO: optimize this! - if (offset === undefined) { - offset = 0; - } - var arr = new Uint8Array(num - offset); - for (var i = offset; i < num; ++i) { - arr[i - offset] = {{{ makeGetValue('ptr', 'i', 'i8') }}}; - } - return arr.buffer; -} -Module['TypedArray_copy'] = TypedArray_copy; -#endif - function String_len(ptr) { var i = 0; while ({{{ makeGetValue('ptr', 'i', 'i8') }}}) i++; // Note: should be |!= 0|, technically. But this helps catch bugs with undefineds @@ -762,17 +729,6 @@ function String_len(ptr) { } Module['String_len'] = String_len; -// Copies a C-style string, terminated by a zero, from the HEAP into -// a normal JavaScript array of numbers -function String_copy(ptr, addZero) { - var len = String_len(ptr); - if (addZero) len++; - var ret = Array_copy(ptr, len); - if (addZero) ret[len-1] = 0; - return ret; -} -Module['String_copy'] = String_copy; - // Tools // This processes a JS string into a C-line array of numbers, 0-terminated. @@ -864,6 +820,7 @@ function addRunDependency() { Module['monitorRunDependencies'](runDependencies); } } +Module['addRunDependency'] = addRunDependency; function removeRunDependency() { runDependencies--; if (Module['monitorRunDependencies']) { @@ -871,6 +828,7 @@ function removeRunDependency() { } if (runDependencies == 0) run(); } +Module['removeRunDependency'] = removeRunDependency; // === Body === diff --git a/src/settings.js b/src/settings.js index e0286213..2526081b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -205,8 +205,6 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as // LINKABLE of 0 is very useful in that we can reduce the size of the // generated code very significantly, by removing everything not actually used. -var GENERATING_HTML = 0; // Set to 1 when generating .html and not just .js - var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This // increases the size of the generated script, but allows you // to more easily perform operations from handwritten JS on diff --git a/src/shell.html b/src/shell.html index dad50b9f..c04ae84b 100644 --- a/src/shell.html +++ b/src/shell.html @@ -23,6 +23,8 @@ <script type='text/javascript'> // connect to canvas var Module = { + preRun: [], + postRun: [], print: (function() { var element = document.getElementById('output'); element.value = ''; // clear browser cache @@ -63,7 +65,7 @@ totalDependencies: 0, monitorRunDependencies: function(left) { this.totalDependencies = Math.max(this.totalDependencies, left); - Module.setStatus(left ? 'Downloading: ' + (this.totalDependencies-left) + '/' + this.totalDependencies + '...' : 'All downloads complete.'); + Module.setStatus(left ? 'Preparing: ' + (this.totalDependencies-left) + '/' + this.totalDependencies + '...' : 'All downloads complete.'); } }; Module.setStatus('Downloading...'); diff --git a/src/shell.js b/src/shell.js index e48be3b0..891a6328 100644 --- a/src/shell.js +++ b/src/shell.js @@ -46,7 +46,7 @@ if (ENVIRONMENT_IS_NODE) { } } else if (ENVIRONMENT_IS_SHELL) { Module['print'] = print; - Module['printErr'] = printErr; + if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm // Polyfill over SpiderMonkey/V8 differences if (typeof read != 'undefined') { @@ -104,11 +104,11 @@ if (!Module['load'] == 'undefined' && Module['read']) { globalEval(Module['read'](f)); }; } -if (!Module['printErr']) { - Module['printErr'] = function(){}; -} if (!Module['print']) { - Module['print'] = Module['printErr']; + Module['print'] = function(){}; +} +if (!Module['printErr']) { + Module['printErr'] = Module['print']; } if (!Module['arguments']) { Module['arguments'] = []; @@ -119,6 +119,10 @@ if (!Module['arguments']) { Module.print = Module['print']; Module.printErr = Module['printErr']; +// Callbacks +if (!Module['preRun']) Module['preRun'] = []; +if (!Module['postRun']) Module['postRun'] = []; + {{BODY}} // {{MODULE_ADDITIONS}} diff --git a/system/include/emscripten.h b/system/include/emscripten/emscripten.h index 4da31ec3..5b71ce6a 100644 --- a/system/include/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -12,6 +12,16 @@ extern "C" { #endif /* + * Forces LLVM to not dead-code-eliminate a function. Note that + * closure may still eliminate it at the JS level, for which you + * should use EXPORTED_FUNCTIONS (see settings.js). + * + * Example usage: + * void EMSCRIPTEN_KEEPALIVE my_function() { .. } + */ +#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) + +/* * Interface to the underlying JS engine. This function will * eval() the given script. */ @@ -50,7 +60,7 @@ extern void emscripten_cancel_main_loop(); #if EMSCRIPTEN extern void emscripten_async_call(void (*func)(), int millis); #else -void emscripten_async_call(void (*func)(), int millis) { +inline void emscripten_async_call(void (*func)(), int millis) { SDL_Delay(millis); func(); } @@ -80,6 +90,11 @@ void emscripten_set_canvas_size(int width, int height); float emscripten_get_now(); /* + * Simple random number generation in [0, 1), maps to Math.random(). + */ +float emscripten_random(); + +/* * This macro-looking function will cause Emscripten to * generate a comment in the generated code. * XXX This is deprecated for now, because it requires us to @@ -88,6 +103,18 @@ float emscripten_get_now(); //extern void EMSCRIPTEN_COMMENT(const char *text); /* + * Emscripten file system api + */ + +/* + * Load file from url in asynchronous way. + * When file is loaded then 'onload' callback will called. + * If any error occurred 'onerror' will called. + * The callbacks are called with the file as their argument. + */ +void emscripten_async_wget(const char* url, const char* file, void (*onload)(const char*), void (*onerror)(const char*)); + +/* * Profiling tools. * INIT must be called first, with the maximum identifier that * will be used. BEGIN will add some code that marks diff --git a/system/include/libc/sys/_default_fcntl.h b/system/include/libc/sys/_default_fcntl.h index 0f2ffb07..188e25c4 100644 --- a/system/include/libc/sys/_default_fcntl.h +++ b/system/include/libc/sys/_default_fcntl.h @@ -209,6 +209,11 @@ extern int _open64 _PARAMS ((const char *, int, ...)); #define POSIX_FADV_DONTNEED 135 int posix_fadvise(int fd, off_t offset, off_t len, int advice); int posix_fallocate(int fd, off_t offset, off_t len); +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_UN 4 +#define LOCK_NB 8 +int flock(int fd, int operation); #ifdef __cplusplus } diff --git a/system/include/libc/sys/dirent.h b/system/include/libc/sys/dirent.h index 1fbe2b21..9dcf34d1 100644 --- a/system/include/libc/sys/dirent.h +++ b/system/include/libc/sys/dirent.h @@ -26,6 +26,10 @@ long telldir(DIR *); DIR *readdir(DIR *); int closedir(DIR *dirp); void rewinddir(DIR *dirp); +int scandir(const char *dirp, + struct dirent ***namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)); enum { DT_UNKNOWN = 0, diff --git a/system/include/sys/statvfs.h b/system/include/sys/statvfs.h index cf0a8c96..192be153 100644 --- a/system/include/sys/statvfs.h +++ b/system/include/sys/statvfs.h @@ -20,7 +20,7 @@ struct statvfs { int f_namemax; }; -int statvfs(char *path, struct statvfs *s); +int statvfs(const char *path, struct statvfs *s); #ifdef __cplusplus } diff --git a/tests/aniso.c b/tests/aniso.c new file mode 100644 index 00000000..e673e228 --- /dev/null +++ b/tests/aniso.c @@ -0,0 +1,210 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +int hasext(const char *exts, const char *ext) // from cube2, zlib licensed +{ + int len = strlen(ext); + if(len) for(const char *cur = exts; (cur = strstr(cur, ext)); cur += len) + { + if((cur == exts || cur[-1] == ' ') && (cur[len] == ' ' || !cur[len])) return 1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 600, 600, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Check extensions + + const char *exts = (const char *)glGetString(GL_EXTENSIONS); + assert(hasext(exts, "GL_EXT_texture_filter_anisotropic")); + + GLint aniso; + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso); + printf("Max anisotropy: %d (using that)\n", aniso); + assert(aniso >= 4); + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 600, 600 ); + + glMatrixMode( GL_PROJECTION ); + GLfloat matrixData[] = { 2.0/600, 0, 0, 0, + 0, -2.0/600, 0, 0, + 0, 0, -2.0/600, 0, + -1, 1, 0, 1 }; + glLoadMatrixf(matrixData); // test loadmatrix + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + + // Load the OpenGL texture + + GLuint texture, texture2; + + const int DDS_SIZE = 43920; + FILE *dds = fopen("water.dds", "rb"); + assert(dds); + char *ddsdata = (char*)malloc(DDS_SIZE); + assert(fread(ddsdata, 1, DDS_SIZE, dds) == DDS_SIZE); + fclose(dds); + + { + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + + char *curr = ddsdata + 128; + int level = 0; + int w = 512; + int h = 64; + while (level < 5) { + printf("uploading level %d: %d, %d\n", level, w, h); + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, w, h, 0, w*h, curr); + assert(!glGetError()); + curr += MAX(w, 4)*MAX(h, 4); + w /= 2; + h /= 2; + level++; + } + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + { + glGenTextures( 1, &texture2 ); + glBindTexture( GL_TEXTURE_2D, texture2 ); + + char *curr = ddsdata + 128; + int level = 0; + int w = 512; + int h = 64; + while (level < 5) { + printf("uploading level %d: %d, %d\n", level, w, h); + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, w, h, 0, w*h, curr); + assert(!glGetError()); + curr += MAX(w, 4)*MAX(h, 4); + w /= 2; + h /= 2; + level++; + } + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso); + } + + // Prepare and Render + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + int w = 10; + int n = 15; + glBindTexture( GL_TEXTURE_2D, texture ); + for (int x = 0; x < n; x++) { + int start = x*w*2; + glBegin( GL_TRIANGLES ); + glTexCoord2i( 1, 0 ); glVertex3f( start , 0, 0 ); + glTexCoord2i( 0, 0 ); glVertex3f( start+w, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( start-w, 300, 0 ); + glEnd(); + } + glBindTexture( GL_TEXTURE_2D, texture2 ); + for (int x = 0; x < n; x++) { + int start = n*w*2 + x*w*2; + glBegin( GL_TRIANGLES ); + glTexCoord2i( 1, 0 ); glVertex3f( start , 0, 0 ); + glTexCoord2i( 0, 0 ); glVertex3f( start+w, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( start-w, 300, 0 ); + glEnd(); + } +/* + int w = 8; + int n = 20; + for (int x = 0; x < n; x++) { + for (int y = 0; y < n*2; y++) { + glBindTexture( GL_TEXTURE_2D, texture ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( x*w, y*(w), 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( (x+1)*(w-2*y/n), y*(w), 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( (x+1)*(w-2*y/n), (y+1)*(w), 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( x*w, (y+1)*(w), 0 ); + glEnd(); + glBindTexture( GL_TEXTURE_2D, texture2 ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( n*w + x*w, y*(w), 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( n*w + (x+1)*(w-2*y/n), y*(w), 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( n*w + (x+1)*(w-2*y/n), (y+1)*(w), 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( n*w + x*w, (y+1)*(w), 0 ); + glEnd(); + } + } +*/ + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(2000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/aniso.png b/tests/aniso.png Binary files differnew file mode 100644 index 00000000..5f5812d2 --- /dev/null +++ b/tests/aniso.png diff --git a/tests/bloom.dds b/tests/bloom.dds Binary files differnew file mode 100644 index 00000000..ed51ab51 --- /dev/null +++ b/tests/bloom.dds diff --git a/tests/cubegeom_fog.c b/tests/cubegeom_fog.c new file mode 100644 index 00000000..9c04a55d --- /dev/null +++ b/tests/cubegeom_fog.c @@ -0,0 +1,307 @@ +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void verify() { + int width = 640, height = 480; + unsigned char *data = (unsigned char*)malloc(width*height*4); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + int sum = 0; + for (int x = 0; x < width*height*4; x++) { + if (x % 4 != 3) sum += x * data[x]; + } +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL ); + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + glClearColor( 0, 0, 0, 0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + // Fog + + glEnable(GL_FOG); + glFogf(GL_FOG_START, 100); + glFogf(GL_FOG_END, 2000); + glFogi(GL_FOG_MODE, GL_LINEAR); + GLfloat fogcolor[4] = { 0.9, 0.1, 0.35, 0 }; + glFogfv(GL_FOG_COLOR, fogcolor); + + // Create a texture + + GLuint texture; + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte textureData[16*16*4]; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + *((int*)&textureData[(x*16 + y) * 4]) = x*16 + ((y*16) << 8); + } + } + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, textureData ); + + // Create a second texture + + GLuint texture2; + glGenTextures( 1, &texture2 ); + glBindTexture( GL_TEXTURE_2D, texture2 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte texture2Data[] = { 0xff, 0, 0, 0xff, + 0, 0xff, 0, 0xaa, + 0, 0, 0xff, 0x55, + 0x80, 0x90, 0x70, 0 }; + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, + GL_RGBA, GL_UNSIGNED_BYTE, texture2Data ); + + // BEGIN + +#if USE_GLEW + glewInit(); +#endif + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // original: glFrustum(-0.6435469817188064, 0.6435469817188064 ,-0.48266022190470925, 0.48266022190470925 ,0.5400000214576721, 2048); + glFrustum(-0.6435469817188064, 0.1435469817188064 ,-0.48266022190470925, 0.88266022190470925 ,0.5400000214576721, 2048); + glRotatef(-30, 1, 1, 1); + //GLfloat pm[] = { 1.372136116027832, 0, 0, 0, 0, 0.7910231351852417, 0, 0, -0.6352481842041016, 0.29297152161598206, -1.0005275011062622, -1, 0, 0, -1.080284833908081, 0 }; + //glLoadMatrixf(pm); + + glMatrixMode(GL_MODELVIEW); + GLfloat matrixData[] = { -1, 0, 0, 0, + 0, 0,-1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 }; + glLoadMatrixf(matrixData); + //glTranslated(-512,-512,-527); // XXX this should be uncommented, but if it is then nothing is shown + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glClear(GL_DEPTH_BUFFER_BIT); + + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glActiveTexture(GL_TEXTURE0); + + glEnableClientState(GL_VERTEX_ARRAY); + + GLuint arrayBuffer, elementBuffer; + glGenBuffers(1, &arrayBuffer); + glGenBuffers(1, &elementBuffer); + + GLubyte arrayData[] = { +/* +[0, 0, 0, 67] ==> 128 float +[0, 0, 128, 67] ==> 256 float +[0, 0, 0, 68] ==> 512 float +[0, 0, 128, 68] ==> 1024 float + +[vertex x ] [vertex y ] [vertex z ] [nr] [texture u ] [texture v ] [lm u ] [lm v ] [color r,g,b,a ] */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 11, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 0 + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 23, 20, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 1 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 35, 30, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 2 + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 47, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 3 + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 51, 50, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 4 + 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 68, 64, 60, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 5 + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 70, 70, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 6 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 89, 80, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 7 + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 94, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 8 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 20, 10, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 9 + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 31, 20, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 10 + 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 68, 42, 30, 0, 0, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 11 + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 53, 40, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 12 + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 64, 50, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 13 + 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 68, 75, 60, 0, 0, 0, 0, 128, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 14 + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 86, 70, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 15 + + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128 + }; + assert(sizeof(arrayData) == 1408); + glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(arrayData), arrayData, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + GLushort elementData[] = { 1, 2, 0, 2, 3, 0, 5, 6, 4, 6, 7, 4, 9, 10, 8, 10, 11, 8, 13, 14, 12, 14, 15, 12 }; + assert(sizeof(elementData) == 48); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elementData), elementData, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer); + + // sauer vertex data is apparently 0-12: V3F, 12: N1B, 16-24: T2F, 24-28: T2S, 28-32: C4B + glVertexPointer(3, GL_FLOAT, 32, (void*)0); // all these apply to the ARRAY_BUFFER that is bound + glTexCoordPointer(2, GL_FLOAT, 32, (void*)16); + glClientActiveTexture(GL_TEXTURE1); // XXX seems to be ignored in native build + glTexCoordPointer(2, GL_SHORT, 32, (void*)24); + glClientActiveTexture(GL_TEXTURE0); // likely not needed, it is a cleanup + glNormalPointer(GL_BYTE, 32, (void*)12); + glColorPointer(4, GL_UNSIGNED_BYTE, 32, (void*)28); + + glBindTexture(GL_TEXTURE_2D, texture); // diffuse? + glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture2); // lightmap? + glActiveTexture(GL_TEXTURE0); + + GLint ok; + + const char *vertexShader = "uniform vec4 texgenscroll;\n" + "void main(void)\n" + "{\n" + " gl_Position = ftransform();\n" + " gl_TexCoord[0].xy = gl_MultiTexCoord0.xy/10000.0 + (0.001*texgenscroll.xy) + gl_Normal.xy;\n" // added /100 here + " gl_TexCoord[1].xy = gl_MultiTexCoord1.xy/100.0 * 3.051851e-05;\n" + " gl_FogFragCoord = gl_Position.z;\n" + "}\n"; + const char *fragmentShader = "uniform vec4 colorparams;\n" + "uniform sampler2D diffusemap, lightmap;\n" + "void main(void)\n" + "{\n" + " vec4 diffuse = texture2D(diffusemap, gl_TexCoord[0].xy);\n" + " vec4 lm = texture2D(lightmap, gl_TexCoord[1].xy);\n" + " diffuse *= colorparams;\n" + " vec4 color = diffuse * lm;\n" + " gl_FragColor.rgb = mix((gl_Fog.color).rgb, color.rgb, clamp((gl_Fog.end - gl_FogFragCoord) * gl_Fog.scale, 0.0, 1.0));\n" + //" gl_FragColor.rgb = 0.0001 * color.rgb + ((gl_Fog.color).rgb * (1.0-clamp((gl_FogFragCoord)* 1.0/1000.0, 0.0, 1.0)));\n" + "}\n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fragmentShader, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint program = glCreateProgram(); + + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &ok); + assert(ok); + + glUseProgram(program); + + GLint lightmapLocation = glGetUniformLocation(program, "lightmap"); + assert(lightmapLocation >= 0); + glUniform1i(lightmapLocation, 1); // sampler2D? Is it the texture unit? + + GLint diffusemapLocation = glGetUniformLocation(program, "diffusemap"); + assert(diffusemapLocation >= 0); + glUniform1i(diffusemapLocation, 0); + + GLint texgenscrollLocation = glGetUniformLocation(program, "texgenscroll"); + assert(texgenscrollLocation >= 0); + + GLint colorparamsLocation = glGetUniformLocation(program, "colorparams"); + assert(colorparamsLocation >= 0); + + GLfloat texgenscrollData[] = { 0, 0, 0, 0 }; + glUniform4fv(texgenscrollLocation, 1, texgenscrollData); + + GLfloat colorparamsData[] = { 2, 2, 2, 1 }; + glUniform4fv(colorparamsLocation, 1, colorparamsData); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)12); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*) 0); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)24); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void*)36); + + // END + + SDL_GL_SwapBuffers(); + + verify(); + +#if !EMSCRIPTEN + SDL_Delay(1500); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/cubegeom_normal_dap_far_glda_quad.c b/tests/cubegeom_normal_dap_far_glda_quad.c new file mode 100644 index 00000000..aa6383b8 --- /dev/null +++ b/tests/cubegeom_normal_dap_far_glda_quad.c @@ -0,0 +1,280 @@ +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void verify() { + int width = 640, height = 480; + unsigned char *data = (unsigned char*)malloc(width*height*4); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + int sum = 0; + for (int x = 0; x < width*height*4; x++) { + if (x % 4 != 3) sum += x * data[x]; + } +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL ); + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + glClearColor( 0, 0, 0, 0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + // Create a texture + + GLuint texture; + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte textureData[16*16*4]; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + *((int*)&textureData[(x*16 + y) * 4]) = x*16 + ((y*16) << 8); + } + } + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, + GL_RGBA, GL_UNSIGNED_BYTE, textureData ); + + // Create a second texture + + GLuint texture2; + glGenTextures( 1, &texture2 ); + glBindTexture( GL_TEXTURE_2D, texture2 ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + GLubyte texture2Data[] = { 0xff, 0, 0, 0xff, + 0, 0xff, 0, 0xaa, + 0, 0, 0xff, 0x55, + 0x80, 0x90, 0x70, 0 }; + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, + GL_RGBA, GL_UNSIGNED_BYTE, texture2Data ); + + // BEGIN + +#if USE_GLEW + glewInit(); +#endif + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // original: glFrustum(-0.6435469817188064, 0.6435469817188064 ,-0.48266022190470925, 0.48266022190470925 ,0.5400000214576721, 2048); + glFrustum(-0.6435469817188064, 0.1435469817188064 ,-0.48266022190470925, 0.88266022190470925 ,0.5400000214576721, 2048); + glRotatef(-30, 1, 1, 1); + //GLfloat pm[] = { 1.372136116027832, 0, 0, 0, 0, 0.7910231351852417, 0, 0, -0.6352481842041016, 0.29297152161598206, -1.0005275011062622, -1, 0, 0, -1.080284833908081, 0 }; + //glLoadMatrixf(pm); + + glMatrixMode(GL_MODELVIEW); + GLfloat matrixData[] = { -1, 0, 0, 0, + 0, 0,-1, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 }; + glLoadMatrixf(matrixData); + //glTranslated(-512,-512,-527); // XXX this should be uncommented, but if it is then nothing is shown + + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + glClear(GL_DEPTH_BUFFER_BIT); + + glEnableClientState(GL_NORMAL_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glActiveTexture(GL_TEXTURE0); + + glEnableClientState(GL_VERTEX_ARRAY); + + GLubyte arrayData[] = { +/* +[0, 0, 0, 67] ==> 128 float +[0, 0, 128, 67] ==> 256 float +[0, 0, 0, 68] ==> 512 float +[0, 0, 128, 68] ==> 1024 float + +[vertex x ] [vertex y ] [vertex z ] [nr] [texture u ] [texture v ] [lm u ] [lm v ] [color r,g,b,a ] */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // ignorable ones + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + + 0, 0, 10, 0, 0, 0, 40, 0, 0, 0, 10, 68, 11, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 0 + 0, 0, 20, 68, 0, 0, 50, 0, 0, 0, 20, 68, 23, 20, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 1 + 0, 0, 30, 68, 0, 0, 60, 68, 0, 0, 30, 68, 35, 30, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 2 + 0, 0, 40, 0, 0, 0, 70, 68, 0, 0, 40, 68, 47, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 3 + 0, 0, 50, 68, 0, 0, 80, 0, 0, 0, 50, 68, 51, 50, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 4 + 0, 0, 128, 68, 0, 0, 90, 0, 0, 0, 60, 68, 64, 60, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, // 5 + 0, 0, 128, 68, 0, 0, 100, 68, 0, 0, 70, 68, 70, 70, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 6 + 0, 0, 60, 68, 0, 0, 110, 68, 0, 0, 80, 68, 89, 80, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 7 + 0, 0, 70, 0, 0, 0, 10, 68, 0, 0, 90, 68, 94, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 8 + 0, 0, 80, 68, 0, 0, 20, 68, 0, 0, 100, 68, 20, 10, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 9 + 0, 0, 90, 68, 0, 0, 128, 68, 0, 0, 110, 68, 31, 20, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 10 + 0, 0, 100, 0, 0, 0, 128, 68, 0, 0, 120, 68, 42, 30, 0, 0, 0, 0, 0, 0, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 11 + 0, 0, 110, 68, 0, 0, 55, 68, 0, 0, 5, 68, 53, 40, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 12 + 0, 0, 128, 68, 0, 0, 75, 68, 0, 0, 15, 68, 64, 50, 0, 0, 0, 0, 128, 67, 0, 0, 0, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 13 + 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 25, 68, 75, 60, 0, 0, 0, 0, 128, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 14 + 0, 0, 55, 68, 0, 0, 128, 68, 0, 0, 55, 68, 86, 70, 0, 0, 0, 0, 0, 67, 0, 0, 128, 67, 0, 0, 0, 0, 128, 128, 128, 128, // 15 + + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 128, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, + }; + + // sauer vertex data is apparently 0-12: V3F, 12: N1B, 16-24: T2F, 24-28: T2S, 28-32: C4B + glVertexPointer(3, GL_FLOAT, 32, (void*)&arrayData[0]); + glTexCoordPointer(2, GL_FLOAT, 32, (void*)&arrayData[16]); + glClientActiveTexture(GL_TEXTURE1); // XXX seems to be ignored in native build + glTexCoordPointer(2, GL_SHORT, 32, (void*)&arrayData[24]); + glClientActiveTexture(GL_TEXTURE0); // likely not needed, it is a cleanup + glNormalPointer(GL_BYTE, 32, (void*)&arrayData[12]); + glColorPointer(4, GL_UNSIGNED_BYTE, 32, (void*)&arrayData[28]); + + glBindTexture(GL_TEXTURE_2D, texture); // diffuse? + glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, texture2); // lightmap? + glActiveTexture(GL_TEXTURE0); + + GLint ok; + + const char *vertexShader = "uniform vec4 texgenscroll;\n" + "void main(void)\n" + "{\n" + " gl_Position = ftransform();\n" + " gl_TexCoord[0].xy = gl_MultiTexCoord0.xy/10000.0 + (0.001*texgenscroll.xy) + gl_Normal.xy;\n" // added /100 here + " gl_TexCoord[1].xy = gl_MultiTexCoord1.xy/100.0 * 3.051851e-05;\n" + "}\n"; + const char *fragmentShader = "uniform vec4 colorparams;\n" + "uniform sampler2D diffusemap, lightmap;\n" + "void main(void)\n" + "{\n" + " vec4 diffuse = texture2D(diffusemap, gl_TexCoord[0].xy);\n" + " vec4 lm = texture2D(lightmap, gl_TexCoord[1].xy);\n" + " diffuse *= colorparams;\n" + " gl_FragColor = diffuse * lm;\n" + "}\n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fragmentShader, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint program = glCreateProgram(); + + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &ok); + assert(ok); + + glUseProgram(program); + + GLint lightmapLocation = glGetUniformLocation(program, "lightmap"); + assert(lightmapLocation >= 0); + glUniform1i(lightmapLocation, 1); // sampler2D? Is it the texture unit? + + GLint diffusemapLocation = glGetUniformLocation(program, "diffusemap"); + assert(diffusemapLocation >= 0); + glUniform1i(diffusemapLocation, 0); + + GLint texgenscrollLocation = glGetUniformLocation(program, "texgenscroll"); + assert(texgenscrollLocation >= 0); + + GLint colorparamsLocation = glGetUniformLocation(program, "colorparams"); + assert(colorparamsLocation >= 0); + + GLfloat texgenscrollData[] = { 0, 0, 0, 0 }; + glUniform4fv(texgenscrollLocation, 1, texgenscrollData); + + GLfloat colorparamsData[] = { 2, 2, 2, 1 }; + glUniform4fv(colorparamsLocation, 1, colorparamsData); + + glDrawArrays(GL_QUADS, 16, 12); + + // END + + SDL_GL_SwapBuffers(); + + verify(); + +#if !EMSCRIPTEN + SDL_Delay(1500); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/emscripten_fs_api_browser.cpp b/tests/emscripten_fs_api_browser.cpp new file mode 100644 index 00000000..07469f34 --- /dev/null +++ b/tests/emscripten_fs_api_browser.cpp @@ -0,0 +1,69 @@ +#include<stdio.h> +#include<emscripten.h> +#include<assert.h> +#include <string.h> + +extern "C" { + +int result = 1; +int get_count = 0; + +void wait_wgets() { + if (get_count == 2) { + emscripten_cancel_main_loop(); + REPORT_RESULT(); + } +} + +void onLoaded(const char* file) { + if (strcmp(file, "/tmp/test.html")) { + result = 0; + } + + printf("loaded: %s\n", file); + + if (FILE * f = fopen(file, "r")) { + printf("exists: %s\n", file); + int c = fgetc (f); + if (c == EOF) { + printf("file empty: %s\n", file); + result = 0; + } + fclose(f); + } else { + result = 0; + printf("!exists: %s\n", file); + } + + get_count++; +} + +void onError(const char* file) { + if (strcmp(file, "/tmp/null")) { + result = 0; + } + + printf("error: %s\n", file); + get_count++; +} + +int main() { + emscripten_async_wget( + "http://localhost:8888/this_is_not_a_file", + "/tmp/null", + onLoaded, + onError); + + emscripten_async_wget( + "http://localhost:8888/test.html", + "/tmp/test.html", + onLoaded, + onError); + + emscripten_set_main_loop(wait_wgets, 0); + + return 0; +} + +} + diff --git a/tests/fcntl/src.c b/tests/fcntl/src.c index 5b40ec79..c8c71c8a 100644 --- a/tests/fcntl/src.c +++ b/tests/fcntl/src.c @@ -45,7 +45,7 @@ int main() { printf("\n"); errno = 0; - flock lk; + struct flock lk; lk.l_type = 42; printf("F_GETLK: %d\n", fcntl(f, F_GETLK, &lk)); printf("errno: %d\n", errno); diff --git a/tests/gl_matrix_identity.c b/tests/gl_matrix_identity.c new file mode 100644 index 00000000..98b1c21f --- /dev/null +++ b/tests/gl_matrix_identity.c @@ -0,0 +1,129 @@ +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void verify() { + int width = 640, height = 480; + unsigned char *data = (unsigned char*)malloc(width*height*4); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + int sum = 0; + for (int x = 0; x < width*height*4; x++) { + if (x % 4 != 3) sum += x * data[x]; + } +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL ); + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Create a texture + + GLuint texture; + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + GLubyte textureData[] = { 0x7f, 0, 0, + 0, 0xff, 0, + 0x7f, 0, 0, + 0, 0xff, 0}; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_BYTE, textureData ); + + + // BEGIN + +#if USE_GLEW + glewInit(); +#endif + + glClearColor( 0, 0, 0.5, 1.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + glColor4f(0.8, 0.8, 0.8, 1); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + const int kRowSize = 20; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + float fbuf[] = {0, 1, 0, 0, 1, + 1, 1, 0, 1, 1, + 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, + 0, 0, 0, 0, 0}; + glBufferData(GL_ARRAY_BUFFER, sizeof(fbuf) * sizeof(float), fbuf, GL_STATIC_DRAW); + + glTexCoordPointer(2, GL_FLOAT, kRowSize, (GLvoid*)(3*4)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(3, GL_FLOAT, kRowSize, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + // END + + SDL_GL_SwapBuffers(); + + verify(); + +#if !EMSCRIPTEN + SDL_Delay(1500); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index 779e51da..8bff717d 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -259,12 +259,14 @@ process(sys.argv[1]) def run_native(self, filename, args): Popen([filename+'.native'] + args, stdout=PIPE).communicate()[0] - def assertIdentical(self, x, y): - if x != y: - raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % ( - limit_size(x), limit_size(y), - limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')])) - )) + def assertIdentical(self, values, y): + if type(values) not in [list, tuple]: values = [values] + for x in values: + if x == y: return # success + raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % ( + limit_size(values[0]), limit_size(y), + limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')])) + )) def assertContained(self, values, string, additional_info=''): if type(values) not in [list, tuple]: values = [values] @@ -1208,6 +1210,35 @@ m_divisor is 1091269979 ''' self.do_run(src, '*1,10,10.5,1,1.2340,0.00*') + def test_globaldoubles(self): + src = r''' + #include <stdlib.h> + #include <stdio.h> + + double testVu, testVv, testWu, testWv; + + void Test(double _testVu, double _testVv, double _testWu, double _testWv) + { + testVu = _testVu; + testVv = _testVv; + testWu = _testWu; + testWv = _testWv; + printf("BUG?\n"); + printf("Display: Vu=%f Vv=%f Wu=%f Wv=%f\n", testVu, testVv, testWu, testWv); + } + + int main(void) + { + double v1 = 465.1; + double v2 = 465.2; + double v3 = 160.3; + double v4 = 111.4; + Test(v1, v2, v3, v4); + return 0; + } + ''' + self.do_run(src, 'BUG?\nDisplay: Vu=465.100000 Vv=465.200000 Wu=160.300000 Wv=111.400000') + def test_math(self): src = ''' #include <stdio.h> @@ -2447,10 +2478,15 @@ m_divisor is 1091269979 #include <stdio.h> #include "emscripten.h" + extern "C" { + void EMSCRIPTEN_KEEPALIVE save_me_aimee() { printf("mann\n"); } + } + int main() { // EMSCRIPTEN_COMMENT("hello from the source"); emscripten_run_script("Module.print('hello world' + '!')"); printf("*%d*\n", emscripten_run_script_int("5*20")); + emscripten_run_script("_save_me_aimee()"); return 0; } ''' @@ -2461,7 +2497,7 @@ def process(filename): # TODO: restore this (see comment in emscripten.h) assert '// hello from the source' in src ''' - self.do_run(src, 'hello world!\n*100*', post_build=check) + self.do_run(src, 'hello world!\n*100*\nmann\n', post_build=check) def test_inlinejs(self): src = r''' @@ -4023,8 +4059,9 @@ def process(filename): var Module = { 'noFSInit': true, 'preRun': function() { - FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory FS.createLazyFile('/', 'test.file', 'test.file', true, false); + // Test FS_* exporting + Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory var test_files_input = 'hi there!'; var test_files_input_index = 0; FS.init(function() { @@ -4779,6 +4816,43 @@ def process(filename): self.do_run(src, '*15,15*\n*15,10*\n*6,10*\n*10,0*\n*7,1*') + def test_phiundef(self): + src = r''' +#include <stdlib.h> +#include <stdio.h> + +static int state; + +struct my_struct { + union { + struct { + unsigned char a; + unsigned char b; + } c; + unsigned int d; + } e; + unsigned int f; +}; + +int main(int argc, char **argv) { + struct my_struct r; + + state = 0; + + for (int i=0;i<argc+10;i++) + { + if (state % 2 == 0) + r.e.c.a = 3; + else + printf("%d\n", r.e.c.a); + state++; + } + return 0; +} + ''' + + self.do_run(src, '3\n3\n3\n3\n3\n') + # libc++ tests def test_iostream(self): @@ -6818,6 +6892,30 @@ f.close() Popen(['python', EMCC, os.path.join(self.get_dir(), 'foo', 'main.o'), os.path.join(self.get_dir(), 'bar', 'main.o')]).communicate() self.assertContained('hello there', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_remove_duplicates(self): + # can happen with .a files. we do a best-effort, removing dupes + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include<stdio.h> + void printey() { printf("bye bye\\n"); } + int main() { + printey(); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'side.cpp'), 'w').write(''' + #include<stdio.h> + void printey() { printf("bye bye\\n"); } + ''') + + # without --remove-duplicates, we fail + err = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'side.cpp')], stderr=PIPE).communicate()[1] + assert not os.path.exists('a.out.js') + assert 'multiply' in err + + # with it, we succeed + err = Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), os.path.join(self.get_dir(), 'side.cpp'), '--remove-duplicates'], stderr=PIPE).communicate()[1] + self.assertContained('bye bye', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_embed_file(self): open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''hello from a file with lots of data and stuff in it thank you very much''') open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' @@ -6836,6 +6934,10 @@ f.close() Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + # preload twice, should not err + Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_multidynamic_link(self): # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols # A workaround is to use --ignore-dynamic-linking, see emcc --help for details @@ -6979,10 +7081,47 @@ f.close() open(os.path.join(self.get_dir(), 'a.out.js'), 'w').write(src) assert 'hello from main' in run_js(os.path.join(self.get_dir(), 'a.out.js')), 'main should print when called manually' + def test_prepost2(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include <stdio.h> + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: function() { Module.print('pre-run') }, + }; + ''') + open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' + Module.postRun = function() { Module.print('post-run') }; + ''') + Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_prepre(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include <stdio.h> + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' + var Module = { + preRun: [function() { Module.print('pre-run') }], + }; + ''') + open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' + Module.preRun.push(function() { Module.print('prepre') }); + ''') + Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() + self.assertContained('prepre\npre-run\nhello from main\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_eliminator(self): - input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read() expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read() - output = Popen([NODE_JS, COFFEESCRIPT, VARIABLE_ELIMINATOR], stdin=PIPE, stdout=PIPE).communicate(input)[0] + output = Popen([NODE_JS, COFFEESCRIPT, VARIABLE_ELIMINATOR, path_from_root('tools', 'eliminator', 'eliminator-test.js')], stdout=PIPE).communicate()[0] self.assertIdentical(expected, output) def test_fix_closure(self): @@ -7096,6 +7235,25 @@ fscanfed: 10 - hello code = open('a.out.js').read() assert 'SAFE_HEAP' in code, 'valid -s option had an effect' + def test_crunch(self): + # crunch should not be run if a .crn exists that is more recent than the .dds + shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') + time.sleep(0.1) + Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert os.stat('test.data').st_size < 0.25*os.stat('ship.dds').st_size, 'Compressed should be much smaller than dds' + crunch_time = os.stat('ship.crn').st_mtime + dds_time = os.stat('ship.dds').st_mtime + assert crunch_time > dds_time, 'Crunch is more recent' + # run again, should not recrunch! + time.sleep(0.1) + Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert crunch_time == os.stat('ship.crn').st_mtime, 'Crunch is unchanged' + # update dds, so should recrunch + time.sleep(0.1) + os.utime('ship.dds', None) + Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch=32', '--preload', 'ship.dds'], stdout=open('pre.js', 'w')).communicate() + assert crunch_time < os.stat('ship.crn').st_mtime, 'Crunch was changed' + elif 'browser' in str(sys.argv): # Browser tests. @@ -7176,6 +7334,7 @@ elif 'browser' in str(sys.argv): output = queue.get() break time.sleep(0.1) + self.assertIdentical(expectedResult, output) finally: server.terminate() @@ -7248,9 +7407,9 @@ elif 'browser' in str(sys.argv): img.src = '%s'; }; Module['postRun'] = doReftest; - Module['preRun'] = function() { - setTimeout(doReftest, 0); // if run() throws an exception and postRun is not called, this will kick in - }; + Module['preRun'].push(function() { + setTimeout(doReftest, 1000); // if run() throws an exception and postRun is not called, this will kick in + }); ''' % basename) def test_html(self): @@ -7417,11 +7576,11 @@ elif 'browser' in str(sys.argv): self.run_browser('page.html', '', '/report_result?1') def test_sdl_image(self): - # load an image file, get pixel data + # load an image file, get pixel data. Also O2 coverage for --preload-file shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() + Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?600') def test_sdl_image_compressed(self): @@ -7518,6 +7677,13 @@ elif 'browser' in str(sys.argv): Popen(['python', EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio.c'), '--preload-file', 'sound.ogg', '--preload-file', 'sound2.wav', '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play", "_play2"]']).communicate() self.run_browser('page.html', '', '/report_result?1') + def test_sdl_audio_quickload(self): + open(os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_audio_quickload.c')).read())) + + # use closure to check for a possible bug with closure minifying away newer Audio() attributes + Popen(['python', EMCC, '-O2', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_quickload.c'), '-o', 'page.html', '-s', 'EXPORTED_FUNCTIONS=["_main", "_play"]']).communicate() + self.run_browser('page.html', '', '/report_result?1') + def test_sdl_gl_read(self): # SDL, OpenGL, readPixels open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read())) @@ -7535,7 +7701,7 @@ elif 'browser' in str(sys.argv): # SDL, OpenGL, textures, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-gray-purple.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_sdl_ogl_p(self): @@ -7545,6 +7711,41 @@ elif 'browser' in str(sys.argv): Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_p.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') + def test_sdl_fog_simple(self): + # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-fog-simple.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_simple.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') + + def test_sdl_fog_negative(self): + # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-fog-negative.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_negative.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') + + def test_sdl_fog_density(self): + # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-fog-density.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_density.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') + + def test_sdl_fog_exp2(self): + # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-fog-exp2.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_exp2.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') + + def test_sdl_fog_linear(self): + # SDL, OpenGL, textures, fog, immediate mode. Closure for more coverage + shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) + self.reftest(path_from_root('tests', 'screenshot-fog-linear.png')) + Popen(['python', EMCC, path_from_root('tests', 'sdl_fog_linear.c'), '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + self.run_browser('something.html', 'You should see an image with fog.', '/report_result?0') + def test_worker(self): # Test running in a web worker output = Popen(['python', EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'], stdout=PIPE, stderr=PIPE).communicate() @@ -7625,20 +7826,24 @@ elif 'browser' in str(sys.argv): Popen(['python', EMCC, program, '-o', 'program.html', '--pre-js', 'reftest.js'] + args).communicate() self.run_browser('program.html', '', '/report_result?0') - def btest(self, filename, expected=None, reference=None, args=[]): # TODO: use in all other tests + def btest(self, filename, expected=None, reference=None, reference_slack=0, args=[]): # TODO: use in all other tests if not reference: open(os.path.join(self.get_dir(), filename), 'w').write(self.with_report_result(open(path_from_root('tests', filename)).read())) else: - expected = '0' # 0 pixels difference than reference + expected = [str(i) for i in range(0, reference_slack+1)] shutil.copyfile(path_from_root('tests', filename), os.path.join(self.get_dir(), filename)) self.reftest(path_from_root('tests', reference)) args += ['--pre-js', 'reftest.js'] Popen(['python', EMCC, os.path.join(self.get_dir(), filename), '-o', 'test.html'] + args).communicate() - self.run_browser('test.html', '.', '/report_result?' + expected) + if type(expected) is str: expected = [expected] + self.run_browser('test.html', '.', ['/report_result?' + e for e in expected]) def test_emscripten_api(self): self.btest('emscripten_api_browser.cpp', '1') + def test_emscripten_fs_api(self): + self.btest('emscripten_fs_api_browser.cpp', '1') + def test_gc(self): self.btest('browser_gc.cpp', '1') @@ -7653,6 +7858,9 @@ elif 'browser' in str(sys.argv): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png']) + def test_matrix_identity(self): + self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) + def test_cubegeom_pre(self): self.btest('cubegeom_pre.c', expected='-1472804742') @@ -7663,7 +7871,7 @@ elif 'browser' in str(sys.argv): self.btest('cubegeom_pre3.c', expected='-1472804742') def test_cubegeom(self): - self.btest('cubegeom.c', expected='188641320') + self.btest('cubegeom.c', expected=['188641320', '1522377227']) def test_cubegeom_color(self): self.btest('cubegeom_color.c', expected='588472350') @@ -7683,6 +7891,9 @@ elif 'browser' in str(sys.argv): def test_cubegeom_normal_dap_far_glda(self): # use glDrawArrays self.btest('cubegeom_normal_dap_far_glda.c', expected='-218745386') + def test_cubegeom_normal_dap_far_glda_quad(self): # with quad + self.btest('cubegeom_normal_dap_far_glda_quad.c', expected='1757386625') + def test_cubegeom_mt(self): self.btest('cubegeom_mt.c', expected='-457159152') # multitexture @@ -7692,9 +7903,44 @@ elif 'browser' in str(sys.argv): def test_cubegeom_texturematrix(self): self.btest('cubegeom_texturematrix.c', expected='1297500583') + def test_cubegeom_fog(self): + self.btest('cubegeom_fog.c', expected='1617140399') + def test_cube_explosion(self): self.btest('cube_explosion.c', expected='667220544') + def test_sdl_canvas_palette(self): + self.btest('sdl_canvas_palette.c', reference='sdl_canvas_palette.png') + + def zzztest_sdl_canvas_palette_2(self): # XXX disabled until we have proper automation + open(os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_canvas_palette_2.c')).read())) + open(os.path.join(self.get_dir(), 'pre.js'), 'w').write('Module[\'preRun\'] = function() { SDL.defaults.copyOnLock = false }') + + Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_canvas_palette_2.c'), '-o', 'page.html', '--pre-js', 'pre.js']).communicate() + self.run_browser('page.html', '') + + def test_s3tc(self): + shutil.copyfile(path_from_root('tests', 'screenshot.dds'), os.path.join(self.get_dir(), 'screenshot.dds')) + self.btest('s3tc.c', reference='s3tc.png', args=['--preload-file', 'screenshot.dds']) + + def test_s3tc_crunch(self): + shutil.copyfile(path_from_root('tests', 'ship.dds'), 'ship.dds') + shutil.copyfile(path_from_root('tests', 'bloom.dds'), 'bloom.dds') + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') + Popen(['python', FILE_PACKAGER, 'test.data', '--pre-run', '--crunch', '--preload', 'ship.dds', 'bloom.dds', 'water.dds'], stdout=open('pre.js', 'w')).communicate() + assert os.stat('test.data').st_size < 0.5*(os.stat('ship.dds').st_size+os.stat('bloom.dds').st_size+os.stat('water.dds').st_size), 'Compressed should be smaller than dds' + shutil.move('ship.dds', 'ship.donotfindme.dds') # make sure we load from the compressed + shutil.move('bloom.dds', 'bloom.donotfindme.dds') # make sure we load from the compressed + shutil.move('water.dds', 'water.donotfindme.dds') # make sure we load from the compressed + self.btest('s3tc_crunch.c', reference='s3tc_crunch.png', args=['--pre-js', 'pre.js']) + + def test_aniso(self): + shutil.copyfile(path_from_root('tests', 'water.dds'), 'water.dds') + self.btest('aniso.c', reference='aniso.png', reference_slack=2, args=['--preload-file', 'water.dds']) + + def test_tex_nonbyte(self): + self.btest('tex_nonbyte.c', reference='tex_nonbyte.png') + def test_pre_run_deps(self): # Adding a dependency in preRun will delay run open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' @@ -7807,7 +8053,7 @@ elif 'benchmark' in str(sys.argv): '-s', 'INLINING_LIMIT=0', '-s', 'TOTAL_MEMORY=100*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024', '-o', final_filename] + emcc_args, stdout=PIPE, stderr=self.stderr_redirect).communicate() - assert os.path.exists(final_filename), 'Failed to compile file: ' + '\n'.join(output) + assert os.path.exists(final_filename), 'Failed to compile file: ' + output[0] # Run JS global total_times, tests_done diff --git a/tests/s3tc.c b/tests/s3tc.c new file mode 100644 index 00000000..c2736feb --- /dev/null +++ b/tests/s3tc.c @@ -0,0 +1,158 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int hasext(const char *exts, const char *ext) // from cube2, zlib licensed +{ + int len = strlen(ext); + if(len) for(const char *cur = exts; (cur = strstr(cur, ext)); cur += len) + { + if((cur == exts || cur[-1] == ' ') && (cur[len] == ' ' || !cur[len])) return 1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Check extensions + + const char *exts = (const char *)glGetString(GL_EXTENSIONS); + assert(hasext(exts, "GL_ARB_texture_compression")); + assert(hasext(exts, "GL_EXT_texture_compression_s3tc")); + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + GLfloat matrixData[] = { 2.0/640, 0, 0, 0, + 0, -2.0/480, 0, 0, + 0, 0, -1, 0, + -1, 1, 0, 1 }; + glLoadMatrixf(matrixData); // test loadmatrix + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + + // Load the OpenGL texture + + GLuint texture; + + #define DDS_SIZE 262272 + FILE *dds = fopen("screenshot.dds", "rb"); + char *ddsdata = (char*)malloc(512*512*4);//DDS_SIZE); + assert(fread(ddsdata, 1, DDS_SIZE, dds) == DDS_SIZE); + fclose(dds); + + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 512, 512, 0, DDS_SIZE-128, ddsdata+128); + assert(!glGetError()); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + + // Prepare and Render + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + // Use clientside vertex pointers to render two items + GLfloat vertexData[] = { 0, 0, 10, 10, // texture2, position2 + 1, 0, 300, 10, + 1, 1, 300, 128, + 0, 1, 10, 128, + 0, 0.5, 410, 10, + 1, 0.5, 600, 10, + 1, 1, 630, 200, + 0.5, 1, 310, 250 }; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 4*4, &vertexData[0]); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 4*4, &vertexData[2]); + + glDrawArrays(GL_QUADS, 0, 8); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + // Render the last item using oldschool glBegin etc + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(1500); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/s3tc.png b/tests/s3tc.png Binary files differnew file mode 100644 index 00000000..893ab397 --- /dev/null +++ b/tests/s3tc.png diff --git a/tests/s3tc_crunch.c b/tests/s3tc_crunch.c new file mode 100644 index 00000000..57974109 --- /dev/null +++ b/tests/s3tc_crunch.c @@ -0,0 +1,210 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int hasext(const char *exts, const char *ext) // from cube2, zlib licensed +{ + int len = strlen(ext); + if(len) for(const char *cur = exts; (cur = strstr(cur, ext)); cur += len) + { + if((cur == exts || cur[-1] == ' ') && (cur[len] == ' ' || !cur[len])) return 1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Check extensions + + const char *exts = (const char *)glGetString(GL_EXTENSIONS); + assert(hasext(exts, "GL_ARB_texture_compression")); + assert(hasext(exts, "GL_EXT_texture_compression_s3tc")); + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + GLfloat matrixData[] = { 2.0/640, 0, 0, 0, + 0, -2.0/480, 0, 0, + 0, 0, -1, 0, + -1, 1, 0, 1 }; + glLoadMatrixf(matrixData); // test loadmatrix + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + + // Load the OpenGL textures + + GLuint texture; + + { + const int DDS_SIZE = 65664; + FILE *dds = fopen("ship.dds", "rb"); + assert(dds); + char *ddsdata = (char*)malloc(DDS_SIZE); + assert(fread(ddsdata, 1, DDS_SIZE, dds) == DDS_SIZE); + fclose(dds); + + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 256, 256, 0, DDS_SIZE-128, ddsdata+128); + assert(!glGetError()); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // second texture + + GLuint texture2; + + { + const int DDS_SIZE = 32896; + FILE *dds = fopen("bloom.dds", "rb"); + assert(dds); + char *ddsdata = (char*)malloc(DDS_SIZE); + assert(fread(ddsdata, 1, DDS_SIZE, dds) == DDS_SIZE); + fclose(dds); + + glGenTextures( 1, &texture2 ); + glBindTexture( GL_TEXTURE_2D, texture2 ); + + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 256, 256, 0, DDS_SIZE-128, ddsdata+128); + assert(!glGetError()); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // third, a non-square texture with mipmaps + + GLuint texture3; + + { + const int DDS_SIZE = 43920; + FILE *dds = fopen("water.dds", "rb"); + assert(dds); + char *ddsdata = (char*)malloc(DDS_SIZE); + assert(fread(ddsdata, 1, DDS_SIZE, dds) == DDS_SIZE); + fclose(dds); + + glGenTextures( 1, &texture3 ); + glBindTexture( GL_TEXTURE_2D, texture3 ); + + assert(!glGetError()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 512, 64, 0, 512*64, ddsdata+128); + assert(!glGetError()); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // Prepare and Render + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + // Use clientside vertex pointers to render two items + GLfloat vertexData[] = { 0, 0, 10, 10, // texture2, position2 + 1, 0, 300, 10, + 1, 1, 300, 128, + 0, 1, 10, 128, + 0, 0.5, 410, 10, + 1, 0.5, 600, 10, + 1, 1, 630, 200, + 0.5, 1, 310, 250 }; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 4*4, &vertexData[0]); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 4*4, &vertexData[2]); + + glDrawArrays(GL_QUADS, 0, 4); + + glBindTexture( GL_TEXTURE_2D, texture3 ); + glDrawArrays(GL_QUADS, 4, 4); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + // Render the last item using oldschool glBegin etc + glBindTexture( GL_TEXTURE_2D, texture2 ); + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(1500); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/s3tc_crunch.png b/tests/s3tc_crunch.png Binary files differnew file mode 100644 index 00000000..383d4c5b --- /dev/null +++ b/tests/s3tc_crunch.png diff --git a/tests/screenshot-fog-density.png b/tests/screenshot-fog-density.png Binary files differnew file mode 100644 index 00000000..cd1f6f1b --- /dev/null +++ b/tests/screenshot-fog-density.png diff --git a/tests/screenshot-fog-exp2.png b/tests/screenshot-fog-exp2.png Binary files differnew file mode 100644 index 00000000..cd5e6a63 --- /dev/null +++ b/tests/screenshot-fog-exp2.png diff --git a/tests/screenshot-fog-linear.png b/tests/screenshot-fog-linear.png Binary files differnew file mode 100644 index 00000000..57534566 --- /dev/null +++ b/tests/screenshot-fog-linear.png diff --git a/tests/screenshot-fog-negative.png b/tests/screenshot-fog-negative.png Binary files differnew file mode 100644 index 00000000..5b18a201 --- /dev/null +++ b/tests/screenshot-fog-negative.png diff --git a/tests/screenshot-fog-simple.png b/tests/screenshot-fog-simple.png Binary files differnew file mode 100644 index 00000000..527768fc --- /dev/null +++ b/tests/screenshot-fog-simple.png diff --git a/tests/screenshot.dds b/tests/screenshot.dds Binary files differnew file mode 100644 index 00000000..de31ea11 --- /dev/null +++ b/tests/screenshot.dds diff --git a/tests/sdl_audio_quickload.c b/tests/sdl_audio_quickload.c new file mode 100644 index 00000000..1525d048 --- /dev/null +++ b/tests/sdl_audio_quickload.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <stdlib.h> +#include <SDL/SDL.h> +#include <SDL/SDL_mixer.h> +#include <assert.h> +#include <limits.h> +#include <emscripten.h> + +Mix_Chunk *sound; + +void play() { + int channel = Mix_PlayChannel(-1, sound, 1); + assert(channel == 0); + + int result = 1; + REPORT_RESULT(); +} + +int main(int argc, char **argv) { + SDL_Init(SDL_INIT_AUDIO); + + int ret = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024); + assert(ret == 0); + + Uint16* buffer = (Uint16*)malloc(10*44100*sizeof(Uint16)); + for (Uint32 i = 0; i < 10*44100; ++i) { + buffer[i] = (i * 5) % UINT32_MAX; + } + sound = Mix_QuickLoad_RAW((Uint8*) buffer, 10*44100*sizeof(Uint16)); + assert(sound); + + play(); + + emscripten_run_script("element = document.createElement('input');" + "element.setAttribute('type', 'button');" + "element.setAttribute('value', 'replay!');" + "element.setAttribute('onclick', 'Module[\"_play\"]()');" + "document.body.appendChild(element);"); + + printf("you should one sounds. press the button to replay!\n"); + + return 0; +} + diff --git a/tests/sdl_canvas_palette.c b/tests/sdl_canvas_palette.c new file mode 100644 index 00000000..316aa44a --- /dev/null +++ b/tests/sdl_canvas_palette.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <SDL/SDL.h> +#include <emscripten.h> + +int main() { + SDL_Init(SDL_INIT_VIDEO); + SDL_Surface *screen = SDL_SetVideoMode(600, 400, 8, SDL_HWSURFACE | SDL_HWPALETTE); + + //initialize sdl palette + //with red green and blue + //colors + SDL_Color pal[3]; + pal[0].r = 255; + pal[0].g = 0; + pal[0].b = 0; + pal[0].unused = 0; + + pal[1].r = 0; + pal[1].g = 255; + pal[1].b = 0; + pal[1].unused = 0; + + pal[2].r = 0; + pal[2].g = 0; + pal[2].b = 255; + pal[2].unused = 0; + + SDL_SetColors(screen, pal, 0, 3); + + SDL_FillRect(screen, NULL, 0); + + { + SDL_Rect rect = { 300, 0, 300, 200 }; + SDL_FillRect(screen, &rect, 1); + } + + { + SDL_Rect rect = { 0, 200, 600, 200 }; + SDL_FillRect(screen, &rect, 2); + } + + //changing green color + //to yellow + pal[1].r = 255; + SDL_SetColors(screen, pal, 1, 1); + + { + SDL_Rect rect = { 300, 200, 300, 200 }; + SDL_FillRect(screen, &rect, 1); + } + + printf("you should see red, blue and yellow rectangles\n"); + + SDL_Quit(); + + return 0; +} + diff --git a/tests/sdl_canvas_palette.png b/tests/sdl_canvas_palette.png Binary files differnew file mode 100644 index 00000000..a52844b6 --- /dev/null +++ b/tests/sdl_canvas_palette.png diff --git a/tests/sdl_canvas_palette_2.c b/tests/sdl_canvas_palette_2.c new file mode 100644 index 00000000..db051b2b --- /dev/null +++ b/tests/sdl_canvas_palette_2.c @@ -0,0 +1,77 @@ +#include <stdio.h> +#include <SDL/SDL.h> +#include <emscripten.h> + +static const int COLOR_COUNT = 32; + +static SDL_Surface *screen; +static SDL_Color pal[COLOR_COUNT +1]; + +void initializePalette() { + //initialize sdl palette + //with red green and blue + //colors + pal[0].r = 0; + pal[0].g = 0; + pal[0].b = 0; + pal[0].unused = 0; + + for (int i=1; i< 1 + COLOR_COUNT; i++) { + pal[i].r = 255 / COLOR_COUNT * i; + pal[i].g = 0; + pal[i].b = 0; + pal[i].unused = 0; + } + + SDL_SetColors(screen, pal, 0, 1 + COLOR_COUNT); +} + +void animatePalette() { + SDL_Color temporary; + temporary = pal[1]; + for (int i=2; i< 1 + COLOR_COUNT; i++) { + pal[i-1] = pal[i]; + } + pal[COLOR_COUNT] = temporary; + + SDL_SetColors(screen, pal, 1, COLOR_COUNT); + + //refreshing + SDL_LockSurface(screen); + SDL_UnlockSurface(screen); + + printf("yet another cycle\n"); +} + +int main() { + SDL_Init(SDL_INIT_VIDEO); + screen = SDL_SetVideoMode(600, 400, 8, SDL_HWSURFACE | SDL_HWPALETTE); + + //test empty pallete + SDL_LockSurface(screen); + SDL_UnlockSurface(screen); + + initializePalette(); + + //palette is red yellow blue + SDL_LockSurface(screen); + int size = screen->h * screen->pitch; + char *color = screen->pixels; + int divider = size / COLOR_COUNT; + int i = 0; + while (i < size) { + *color = 1 + (i / divider); //red + color++; + i++; + } + SDL_UnlockSurface(screen); + + //Animation + printf("you should see red gradient animation\n"); + emscripten_set_main_loop(animatePalette, 0); + + SDL_Quit(); + + return 0; +} + diff --git a/tests/sdl_fog_density.c b/tests/sdl_fog_density.c new file mode 100644 index 00000000..95773419 --- /dev/null +++ b/tests/sdl_fog_density.c @@ -0,0 +1,183 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1000, 1000 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glEnable(GL_FOG); + GLfloat fogColor[] = { 1.0, 0.5, 0.5, 0.05 }; + glFogfv(GL_FOG_COLOR, fogColor); + glFogf(GL_FOG_DENSITY, 0.2); + + assert(glIsEnabled(GL_FOG)); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 10 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 10 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 10 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 10 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 5 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 6 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 7 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 8 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 1 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 5 ); + glVertex3f( 300, 410, 50 ); + glVertex3f( 300, 480, 100 ); + glVertex3f( 10, 470, 5 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 10 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 10 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 10 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 10 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(30000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_fog_exp2.c b/tests/sdl_fog_exp2.c new file mode 100644 index 00000000..a09a5e3d --- /dev/null +++ b/tests/sdl_fog_exp2.c @@ -0,0 +1,184 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1000, 1000 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glEnable(GL_FOG); + GLfloat fogColor[] = { 1.0, 0.5, 0.5, 0.05 }; + glFogfv(GL_FOG_COLOR, fogColor); + glFogf(GL_FOG_DENSITY, 0.2); + glFogi(GL_FOG_MODE, GL_EXP2); + + assert(glIsEnabled(GL_FOG)); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 10 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 10 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 10 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 10 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 5 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 6 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 7 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 8 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 1 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 5 ); + glVertex3f( 300, 410, 50 ); + glVertex3f( 300, 480, 100 ); + glVertex3f( 10, 470, 5 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 10 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 10 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 10 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 10 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(30000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_fog_linear.c b/tests/sdl_fog_linear.c new file mode 100644 index 00000000..8fc18b7c --- /dev/null +++ b/tests/sdl_fog_linear.c @@ -0,0 +1,185 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1000, 1000 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glEnable(GL_FOG); + GLfloat fogColor[] = { 1.0, 0.5, 0.5, 0.05 }; + glFogfv(GL_FOG_COLOR, fogColor); + glFogi(GL_FOG_START, 8); + glFogi(GL_FOG_END, 13); + glFogi(GL_FOG_MODE, GL_LINEAR); + + assert(glIsEnabled(GL_FOG)); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 10 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 10 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 10 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 10 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 5 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 6 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 7 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 8 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 1 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 5 ); + glVertex3f( 300, 410, 50 ); + glVertex3f( 300, 480, 100 ); + glVertex3f( 10, 470, 5 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 10 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 10 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 10 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 10 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(30000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_fog_negative.c b/tests/sdl_fog_negative.c new file mode 100644 index 00000000..2d589a47 --- /dev/null +++ b/tests/sdl_fog_negative.c @@ -0,0 +1,182 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1000, 1000 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glEnable(GL_FOG); + GLfloat fogColor[] = { 1.0, 0.5, 0.5, 0.05 }; + glFogfv(GL_FOG_COLOR, fogColor); + + assert(glIsEnabled(GL_FOG)); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, -1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, -1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, -1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, -1 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, -5 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, -6 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, -7 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, -8 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, -1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, -1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, -1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, -1 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, -5 ); + glVertex3f( 300, 410, -50 ); + glVertex3f( 300, 480, -100 ); + glVertex3f( 10, 470, -5 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, -10 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, -10 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, -10 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, -10 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(30000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_fog_simple.c b/tests/sdl_fog_simple.c new file mode 100644 index 00000000..be023593 --- /dev/null +++ b/tests/sdl_fog_simple.c @@ -0,0 +1,182 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1000, 1000 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glEnable(GL_FOG); + GLfloat fogColor[] = { 1.0, 0.5, 0.5, 0.05 }; + glFogfv(GL_FOG_COLOR, fogColor); + + assert(glIsEnabled(GL_FOG)); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 10 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 10 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 10 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 10 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 5 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 6 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 7 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 8 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 1 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 1 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 1 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 1 ); + glEnd(); + +#if !EMSCRIPTEN + glDisable(GL_TEXTURE_2D); +#endif + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 5 ); + glVertex3f( 300, 410, 50 ); + glVertex3f( 300, 480, 100 ); + glVertex3f( 10, 470, 5 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 10 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 10 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 10 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 10 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(30000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/ship.dds b/tests/ship.dds Binary files differnew file mode 100644 index 00000000..dd7b04e9 --- /dev/null +++ b/tests/ship.dds diff --git a/tests/tex_nonbyte.c b/tests/tex_nonbyte.c new file mode 100644 index 00000000..8f2ec162 --- /dev/null +++ b/tests/tex_nonbyte.c @@ -0,0 +1,206 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void shaders() { +#if USE_GLEW + glewInit(); +#endif + + GLint ok; + + const char *vertexShader = "void main(void) \n" + "{ \n" + " gl_Position = ftransform(); \n" + " gl_TexCoord[0] = gl_MultiTexCoord0; \n" + " gl_FrontColor = gl_Color; \n" + "} \n"; + const char *fragmentShader = "uniform sampler2D tex0; \n" + "void main(void) \n" + "{ \n" + " gl_FragColor = gl_Color * texture2D(tex0, gl_TexCoord[0].xy); \n" + "} \n"; + + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fs, 1, &fragmentShader, NULL); + glCompileShader(fs); + glGetShaderiv(fs, GL_COMPILE_STATUS, &ok); + assert(ok); + + GLuint program = glCreateProgram(); + + glAttachShader(program, vs); + glAttachShader(program, fs); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &ok); + assert(ok); + + glUseProgram(program); + + { + // Also, check getting the error log + const char *fakeVertexShader = "atbute ve4 blarg; ### AAA\n"; + GLuint vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &fakeVertexShader, NULL); + glCompileShader(vs); + glGetShaderiv(vs, GL_COMPILE_STATUS, &ok); + assert(!ok); + GLint infoLen = 0; + glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &infoLen); + assert(infoLen > 1); + } +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Set the OpenGL state after creating the context with SDL_SetVideoMode + + glClearColor( 0, 0, 0, 0 ); + +#if !EMSCRIPTEN + glEnable( GL_TEXTURE_2D ); // Need this to display a texture XXX unnecessary in OpenGL ES 2.0/WebGL +#endif + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + GLfloat matrixData[] = { 2.0/640, 0, 0, 0, + 0, -2.0/480, 0, 0, + 0, 0, -1, 0, + -1, 1, 0, 1 }; + glLoadMatrixf(matrixData); // test loadmatrix + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + int w = 256, h = 256; + GLushort *pixels = (GLushort*)malloc(w*h*2); + for (int x = 0; x < w; x++) + for (int y = 0; y < h; y++) + pixels[w*y + x] = ((x*32)/w) | ((((w-x)*(h-y)*64)/(w*h)) << 5) | (((y*32)/h) << 11); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels ); + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + shaders(); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + // Use clientside vertex pointers to render two items + GLfloat vertexData[] = { 0, 0, 10, 10, // texture2, position2 + 1, 0, 300, 10, + 1, 1, 300, 128, + 0, 1, 10, 128, + 0, 0.5, 410, 10, + 1, 0.5, 600, 10, + 1, 1, 630, 200, + 0.5, 1, 310, 250, + 0, 0, 100, 300, + 1, 0, 300, 300, + 1, 1, 300, 400, + 0, 1, 100, 400 }; + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 4*4, &vertexData[0]); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 4*4, &vertexData[2]); + + glDrawArrays(GL_QUADS, 0, 12); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} + diff --git a/tests/tex_nonbyte.png b/tests/tex_nonbyte.png Binary files differnew file mode 100644 index 00000000..8e542417 --- /dev/null +++ b/tests/tex_nonbyte.png diff --git a/tests/water.dds b/tests/water.dds Binary files differnew file mode 100644 index 00000000..2a78a859 --- /dev/null +++ b/tests/water.dds diff --git a/tools/crunch-worker.js b/tools/crunch-worker.js new file mode 100644 index 00000000..5c48d009 --- /dev/null +++ b/tools/crunch-worker.js @@ -0,0 +1,124 @@ +// From Brandon Jones' awesome WebGL texture utils, +// https://github.com/toji/webgl-texture-utils +// which in turn based off of Evan Parker's cool work, +// http://www-cs-students.stanford.edu/~eparker/files/crunch/decode_test.html +// +// This combines crunch-worker.js and crn_decomp.js. Minor changes +// to make it work that way, and to return all levels in one array + + /* + * Copyright (c) 2012 Brandon Jones + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + */ + + // Taken from crnlib.h + var cCRNFmtInvalid = -1; + + var cCRNFmtDXT1 = 0; + var cCRNFmtDXT3 = 1; + var cCRNFmtDXT5 = 2; + + // Various DXT5 derivatives + var cCRNFmtDXT5_CCxY = 3; // Luma-chroma + var cCRNFmtDXT5_xGxR = 4; // Swizzled 2-component + var cCRNFmtDXT5_xGBR = 5; // Swizzled 3-component + var cCRNFmtDXT5_AGBR = 6; // Swizzled 4-component + + // ATI 3DC and X360 DXN + var cCRNFmtDXN_XY = 7; + var cCRNFmtDXN_YX = 8; + + // DXT5 alpha blocks only + var cCRNFmtDXT5A = 9; + + function arrayBufferCopy(src, dst, dstByteOffset, numBytes) { + dst.set(src.subarray(0, numBytes), dstByteOffset); + } + + function deCrunch(bytes, filename) { + var srcSize = bytes.length; + var src = Module._malloc(srcSize), + format, internalFormat, dst, dstSize, + width, height, levels, dxtData, rgb565Data, i; + + arrayBufferCopy(bytes, Module.HEAPU8, src, srcSize); + + format = Module._crn_get_dxt_format(src, srcSize); + + if(format != cCRNFmtDXT1 && format != cCRNFmtDXT3 && format != cCRNFmtDXT5) { + throw "Unsupported image format " + format + " for " + filename; + } + width = Module._crn_get_width(src, srcSize); + height = Module._crn_get_height(src, srcSize); + levels = Module._crn_get_levels(src, srcSize); + dstSize = Module._crn_get_uncompressed_size(src, srcSize, 0); + dst = Module._malloc(dstSize); + + var totalSize = 0; + var bytesPerPixel = format == cCRNFmtDXT1 ? 0.5 : 1; + for(i = 0; i < levels; ++i) { + totalSize += width * height * bytesPerPixel; + width *= 0.5; + height *= 0.5; + width = Math.max(width, 4); + height = Math.max(height, 4); + } + + width = Module._crn_get_width(src, srcSize); + height = Module._crn_get_height(src, srcSize); + + var ret = new Uint8Array(totalSize); + var retIndex = 0; + + for(i = 0; i < levels; ++i) { + if(i) { + dstSize = Module._crn_get_uncompressed_size(src, srcSize, i); + } + Module._crn_decompress(src, srcSize, dst, dstSize, i); + ret.set(Module.HEAPU8.subarray(dst, dst+dstSize), retIndex); + retIndex += dstSize; + + width *= 0.5; + height *= 0.5; + } + + Module._free(src); + Module._free(dst); + + return ret; + } + + //=== +function a(b){throw b}var aa=void 0,l=!0,pa=null,n=!1,za=[],Da="object"===typeof process,Ea="object"===typeof window,Fa="function"===typeof importScripts,Ja=!Ea&&!Da&&!Fa;if(Da){print=(function(b){process.stdout.write(b+"\n")});printErr=(function(b){process.stderr.write(b+"\n")});var Ma=require("fs");read=(function(b){var c=Ma.readFileSync(b).toString();!c&&"/"!=b[0]&&(b=__dirname.split("/").slice(0,-1).join("/")+"/src/"+b,c=Ma.readFileSync(b).toString());return c});load=(function(b){Na(read(b))});za=process.argv.slice(2)}else{Ja?(this.read||(this.read=(function(b){snarf(b)})),"undefined"!=typeof scriptArgs?za=scriptArgs:"undefined"!=typeof arguments&&(za=arguments)):Ea?(this.print=printErr=(function(b){console.log(b)}),this.read=(function(b){var c=new XMLHttpRequest;c.open("GET",b,n);c.send(pa);return c.responseText}),this.arguments&&(za=arguments)):Fa?this.load=importScripts:a("Unknown runtime environment. Where are we?")}function Na(b){eval.call(pa,b)}"undefined"==typeof load&&"undefined"!=typeof read&&(this.load=(function(b){Na(read(b))}));"undefined"===typeof printErr&&(this.printErr=(function(){}));"undefined"===typeof print&&(this.print=printErr);try{this.Module=Module}catch(Qa){this.Module=Module={}}Module.arguments||(Module.arguments=za);Module.print&&(print=Module.print);function Wa(b){if(Xa==1){return 1}var c={"%i1":1,"%i8":1,"%i16":2,"%i32":4,"%i64":8,"%float":4,"%double":8}["%"+b];if(!c){if(b[b.length-1]=="*"){c=Xa}else{if(b[0]=="i"){b=parseInt(b.substr(1));Ya(b%8==0);c=b/8}}}return c}function cb(b){var c=q;q=q+b;q=q+3>>2<<2;return c}function db(b){var c=eb;eb=eb+b;eb=eb+3>>2<<2;if(eb>=fb){for(;fb<=eb;){fb=2*fb+4095>>12<<12}var b=v,d=new ArrayBuffer(fb);v=new Int8Array(d);gb=new Int16Array(d);y=new Int32Array(d);z=new Uint8Array(d);A=new Uint16Array(d);C=new Uint32Array(d);lb=new Float32Array(d);mb=new Float64Array(d);v.set(b)}return c}var Xa=4,ub={},vb;function wb(b){print(b+":\n"+Error().stack);a("Assertion: "+b)}function Ya(b,c){b||wb("Assertion failed: "+c)}var Mb=this;Module.ccall=(function(b,c,d,e){try{var g=eval("_"+b)}catch(h){try{g=Mb.Module["_"+b]}catch(j){}}Ya(g,"Cannot call unknown function "+b+" (perhaps LLVM optimizations or closure removed it?)");var i=0,b=e?e.map((function(b){if(d[i++]=="string"){var c=q;cb(b.length+1);Nb(b,c);b=c}return b})):[];return(function(b,c){return c=="string"?Vb(b):b})(g.apply(pa,b),c)});function Wb(b,c,d){d=d||"i8";d[d.length-1]==="*"&&(d="i32");switch(d){case"i1":v[b]=c;break;case"i8":v[b]=c;break;case"i16":gb[b>>1]=c;break;case"i32":y[b>>2]=c;break;case"i64":y[b>>2]=c;break;case"float":lb[b>>2]=c;break;case"double":Yb[0]=c;y[b>>2]=Zb[0];y[b+4>>2]=Zb[1];break;default:wb("invalid type for setValue: "+d)}}Module.setValue=Wb;Module.getValue=(function(b,c){c=c||"i8";c[c.length-1]==="*"&&(c="i32");switch(c){case"i1":return v[b];case"i8":return v[b];case"i16":return gb[b>>1];case"i32":return y[b>>2];case"i64":return y[b>>2];case"float":return lb[b>>2];case"double":return Zb[0]=y[b>>2],Zb[1]=y[b+4>>2],Yb[0];default:wb("invalid type for setValue: "+c)}return pa});var $b=1,D=2;Module.ALLOC_NORMAL=0;Module.ALLOC_STACK=$b;Module.ALLOC_STATIC=D;function G(b,c,d){var e,g;if(typeof b==="number"){e=l;g=b}else{e=n;g=b.length}var h=typeof c==="string"?c:pa,d=[ac,cb,db][d===aa?D:d](Math.max(g,h?1:c.length));if(e){bc(d,0,g);return d}e=0;for(var j;e<g;){var i=b[e];typeof i==="function"&&(i=ub.xa(i));j=h||c[e];if(j===0){e++}else{j=="i64"&&(j="i32");Wb(d+e,i,j);e=e+Wa(j)}}return d}Module.allocate=G;function Vb(b,c){for(var d=typeof c=="undefined",e="",g=0,h,j=String.fromCharCode(0);;){h=String.fromCharCode(z[b+g]);if(d&&h==j){break}e=e+h;g=g+1;if(!d&&g==c){break}}return e}Module.Pointer_stringify=Vb;Module.Array_stringify=(function(b){for(var c="",d=0;d<b.length;d++){c=c+String.fromCharCode(b[d])}return c});var dc,ec=4096,v,z,gb,A,y,C,lb,mb,q,fc,eb,hc=Module.TOTAL_STACK||5242880,fb=Module.TOTAL_MEMORY||10485760;Ya(!!Int32Array&&!!Float64Array&&!!(new Int32Array(1)).subarray&&!!(new Int32Array(1)).set,"Cannot fallback to non-typed array case: Code is too specialized");var ic=new ArrayBuffer(fb);v=new Int8Array(ic);gb=new Int16Array(ic);y=new Int32Array(ic);z=new Uint8Array(ic);A=new Uint16Array(ic);C=new Uint32Array(ic);lb=new Float32Array(ic);mb=new Float64Array(ic);y[0]=255;Ya(255===z[0]&&0===z[3],"Typed arrays 2 must be run on a little-endian system");var kc=jc("(null)");eb=kc.length;for(var lc=0;lc<kc.length;lc++){v[lc]=kc[lc]}Module.HEAP=aa;Module.HEAP8=v;Module.HEAP16=gb;Module.HEAP32=y;Module.HEAPU8=z;Module.HEAPU16=A;Module.HEAPU32=C;Module.HEAPF32=lb;Module.HEAPF64=mb;fc=(q=4*Math.ceil(eb/4))+hc;var mc=8*Math.ceil(fc/8);v.subarray(mc);var Zb=y.subarray(mc>>2);lb.subarray(mc>>2);var Yb=mb.subarray(mc>>3);fc=mc+8;eb=fc+4095>>12<<12;function nc(b){for(;b.length>0;){var c=b.shift(),d=c.r;typeof d==="number"&&(d=dc[d]);d(c.da===aa?pa:c.da)}}var pc=[],qc=[];function rc(b,c){return Array.prototype.slice.call(v.subarray(b,b+c))}Module.Array_copy=rc;Module.TypedArray_copy=(function(b,c){for(var d=new Uint8Array(c),e=0;e<c;++e){d[e]=v[b+e]}return d.buffer});function sc(b){for(var c=0;v[b+c];){c++}return c}Module.String_len=sc;function tc(b,c){var d=sc(b);c&&d++;var e=rc(b,d);c&&(e[d-1]=0);return e}Module.String_copy=tc;function jc(b,c){for(var d=[],e=0;e<b.length;){var g=b.charCodeAt(e);g>255&&(g=g&255);d.push(g);e=e+1}c||d.push(0);return d}Module.intArrayFromString=jc;Module.intArrayToString=(function(b){for(var c=[],d=0;d<b.length;d++){var e=b[d];e>255&&(e=e&255);c.push(String.fromCharCode(e))}return c.join("")});function Nb(b,c,d){for(var e=0;e<b.length;){var g=b.charCodeAt(e);g>255&&(g=g&255);v[c+e]=g;e=e+1}d||(v[c+e]=0)}Module.writeStringToMemory=Nb;var M=[];function uc(b,c){return b>=0?b:c<=32?2*Math.abs(1<<c-1)+b:Math.pow(2,c)+b}function vc(b,c){if(b<=0){return b}var d=c<=32?Math.abs(1<<c-1):Math.pow(2,c-1);if(b>=d&&(c<=32||b>d)){b=-2*d+b}return b}function wc(b){b=b-1|0;b=b>>>16|b;b=b>>>8|b;b=b>>>4|b;b=b>>>2|b;return(b>>>1|b)+1|0}function xc(b,c){var d=M.O|0,e=q;q=q+512;for(var g=e|0,d=(vb=q,q=q+12,y[vb>>2]=d,y[vb+4>>2]=c,y[vb+8>>2]=b,vb),d=yc(M.G|0,d),h=d.length,j=0;j<h;j++){v[g+j]=d[j]}v[g+j]=0;h=(vb=q,q=q+1,q=q+3>>2<<2,y[vb>>2]=0,vb);d=y[zc>>2];j=yc(g,h);g=q;h=G(j,"i8",$b);j=j.length*1;if(j!=0&&Dc(d,h,j)==-1&&Ec[d]){Ec[d].error=l}q=g;q=e}function Fc(b,c,d,e,g){var h,j,i=q;q=q+4;var f=b+4|0;j=(b+8|0)>>2;C[f>>2]>>>0>C[j]>>>0&&xc(M.H|0,2121);Math.floor(2147418112/(e>>>0))>>>0>c>>>0||xc(M.T|0,2122);var k=C[j],o=k>>>0<c>>>0;do{if(o){var m=d?((c|0)==0?0:(c-1&c|0)==0)?c:wc(c):c;(m|0)!=0&m>>>0>k>>>0||xc(M.Y|0,2131);var t=m*e|0;if((g|0)==0){h=b|0;var s,w=y[h>>2],u=t,r=i;s=q;q=q+4;if((w&7|0)==0){if(u>>>0>2147418112){xc(M.m|0,2500);r=0}else{y[s>>2]=u;w=dc[y[Gc>>2]](w,u,s,1,y[Hc>>2]);(r|0)!=0&&(y[r>>2]=y[s>>2]);(w&7|0)!=0&&xc(M.n|0,2552);r=w}}else{xc(M.J|0,2500);r=0}q=s;s=r;if((s|0)==0){m=0;break}y[h>>2]=s}else{s=Ic(t,i);if((s|0)==0){m=0;break}h=(b|0)>>2;dc[g](s,y[h],y[f>>2]);r=y[h];(r|0)!=0&&Jc(r);y[h]=s}h=C[i>>2];y[j]=h>>>0>t>>>0?Math.floor((h>>>0)/(e>>>0)):m}m=1}while(0);q=i;return m}Fc.X=1;function Ic(b,c){var d=q;q=q+4;var e=b+3&-4,e=(e|0)==0?4:e;if(e>>>0>2147418112){xc(M.m|0,2500);e=0}else{y[d>>2]=e;var g=dc[y[Gc>>2]](0,e,d,1,y[Hc>>2]),h=C[d>>2];(c|0)!=0&&(y[c>>2]=h);if((g|0)==0|h>>>0<e>>>0){xc(M.I|0,2500);e=0}else{(g&7|0)!=0&&xc(M.n|0,2527);e=g}}q=d;return e}function Jc(b){if((b|0)!=0){if((b&7|0)==0){dc[y[Gc>>2]](b,0,0,1,y[Hc>>2])}else{xc(M.K|0,2500)}}}function Kc(b,c,d,e){var g,h,j,i,f=b>>2,k=q;q=q+200;var o;i=k>>2;var m=k+64;j=m>>2;var t=k+132,s=(c|0)==0|e>>>0>11;a:do{if(s){var w=0}else{y[f]=c;bc(m,0,68);for(var u=0;;){var r=z[d+u|0];if(r<<24>>24!=0){var x=((r&255)<<2)+m|0;y[x>>2]=y[x>>2]+1|0}var p=u+1|0;if((p|0)==(c|0)){var B=1,I=-1,S=0,Q=0,H=0;break}u=p}for(;;){var J=C[(B<<2>>2)+j];if((J|0)==0){y[((B-1<<2)+28>>2)+f]=0;var K=H,E=Q,da=S,N=I}else{var ka=I>>>0<B>>>0?I:B,ba=S>>>0>B>>>0?S:B,ra=B-1|0;y[(ra<<2>>2)+i]=H;var ia=J+H|0,fa=16-B|0;y[((ra<<2)+28>>2)+f]=(ia-1<<fa|(1<<fa)-1)+1|0;y[((ra<<2)+96>>2)+f]=Q;y[t+(B<<2)>>2]=Q;K=ia;E=J+Q|0;da=ba;N=ka}var Ga=B+1|0;if((Ga|0)==17){break}B=Ga;I=N;S=da;Q=E;H=K<<1}y[f+1]=E;h=(b+172|0)>>2;if(E>>>0>C[h]>>>0){var ta=((E|0)==0?0:(E-1&E|0)==0)?E:c>>>0<wc(E)>>>0?c:wc(E);y[h]=ta;var la=b+176|0,Aa=y[la>>2];if((Aa|0)==0){var xa=ta}else{Lc(Aa);xa=y[h]}var Y,ma=(xa|0)==0?1:xa,ga=Ic((ma<<1)+8|0,0);if((ga|0)==0){var ua=0}else{var Ba=ga+8|0;y[ga+4>>2]=ma;y[ga>>2]=ma^-1;ua=Ba}Y=ua;y[la>>2]=Y;if((Y|0)==0){w=0;break}var Ca=la}else{Ca=b+176|0}var va=b+24|0;v[va]=N&255;v[b+25|0]=da&255;for(var Z=0;;){var sa=z[d+Z|0],T=sa&255;if(sa<<24>>24!=0){(y[(T<<2>>2)+j]|0)==0&&xc(M.Z|0,2274);var ea=(T<<2)+t|0,W=C[ea>>2];y[ea>>2]=W+1|0;W>>>0<E>>>0||xc(M.$|0,2278);gb[y[Ca>>2]+(W<<1)>>1]=Z&65535}var ya=Z+1|0;if((ya|0)==(c|0)){break}Z=ya}var L=z[va],O=(L&255)>>>0<e>>>0?e:0,qa=b+8|0;y[qa>>2]=O;var na=(O|0)!=0;if(na){var U=1<<O,P=b+164|0,F=U>>>0>C[P>>2]>>>0;do{if(F){y[P>>2]=U;var oa=b+168|0,wa=y[oa>>2];(wa|0)!=0&&Mc(wa);var ca,V=(U|0)==0?1:U,ja=Ic((V<<2)+8|0,0);if((ja|0)==0){var Oa=0}else{var hb=ja+8|0;y[ja+4>>2]=V;y[ja>>2]=V^-1;Oa=hb}ca=Oa;y[oa>>2]=ca;if((ca|0)==0){w=0;break a}bc(ca,-1,U<<2);if((O|0)==0){o=26}else{Ra=oa;o=34}}else{var Ha=b+168|0;bc(y[Ha>>2],-1,U<<2);var Ra=Ha;o=34}}while(0);b:do{if(o==34){for(var Za=1;;){var Ob=(y[(Za<<2>>2)+j]|0)==0;c:do{if(!Ob){var xb=O-Za|0,Pb=1<<xb,yb=Za-1|0,ha=C[(yb<<2>>2)+i],Ka,La=b,ib=Za;(ib|0)!=0&ib>>>0<17||xc(M.W|0,1954);var zb=y[La+(ib-1<<2)+28>>2];Ka=(zb|0)==0?-1:(zb-1|0)>>>((16-ib|0)>>>0);if(ha>>>0<=Ka>>>0){for(var Qb=y[((yb<<2)+96>>2)+f]-ha|0,Rb=Za<<16,$a=ha;;){var Sb=A[y[Ca>>2]+(Qb+$a<<1)>>1]&65535;(z[d+Sb|0]&255|0)!=(Za|0)&&xc(M.aa|0,2320);for(var Tb=$a<<xb,cc=Sb|Rb,Ab=0;;){var bb=Ab+Tb|0;bb>>>0<U>>>0||xc(M.ba|0,2326);var ab=C[Ra>>2];if((y[ab+(bb<<2)>>2]|0)==-1){var Ia=ab}else{xc(M.ca|0,2328);Ia=y[Ra>>2]}y[Ia+(bb<<2)>>2]=cc;var Bb=Ab+1|0;if(Bb>>>0>=Pb>>>0){break}Ab=Bb}var Cb=$a+1|0;if(Cb>>>0>Ka>>>0){break c}$a=Cb}}}}while(0);var Db=Za+1|0;if(Db>>>0>O>>>0){break b}Za=Db}}}while(0);var ob=v[va]}else{ob=L}var pb=b+96|0;y[pb>>2]=y[pb>>2]-y[i]|0;var Eb=b+100|0;y[Eb>>2]=y[Eb>>2]-y[i+1]|0;var Fb=b+104|0;y[Fb>>2]=y[Fb>>2]-y[i+2]|0;var qb=b+108|0;y[qb>>2]=y[qb>>2]-y[i+3]|0;var jb=b+112|0;y[jb>>2]=y[jb>>2]-y[i+4]|0;var rb=b+116|0;y[rb>>2]=y[rb>>2]-y[i+5]|0;var Sa=b+120|0;y[Sa>>2]=y[Sa>>2]-y[i+6]|0;var Pa=b+124|0;y[Pa>>2]=y[Pa>>2]-y[i+7]|0;var sb=b+128|0;y[sb>>2]=y[sb>>2]-y[i+8]|0;var Ta=b+132|0;y[Ta>>2]=y[Ta>>2]-y[i+9]|0;var Gb=b+136|0;y[Gb>>2]=y[Gb>>2]-y[i+10]|0;var Hb=b+140|0;y[Hb>>2]=y[Hb>>2]-y[i+11]|0;var Ib=b+144|0;y[Ib>>2]=y[Ib>>2]-y[i+12]|0;var tb=b+148|0;y[tb>>2]=y[tb>>2]-y[i+13]|0;var Jb=b+152|0;y[Jb>>2]=y[Jb>>2]-y[i+14]|0;var Kb=b+156|0;y[Kb>>2]=y[Kb>>2]-y[i+15]|0;var Lb=b+16|0;y[Lb>>2]=0;g=(b+20|0)>>2;y[g]=ob&255;b:do{if(na){for(var kb=O;;){if((kb|0)==0){break b}var Ua=kb-1|0;if((y[(kb<<2>>2)+j]|0)!=0){break}kb=Ua}y[Lb>>2]=y[((Ua<<2)+28>>2)+f];for(var Ub=O+1|0,Va=y[g]=Ub;;){if(Va>>>0>da>>>0){break b}if((y[(Va<<2>>2)+j]|0)!=0){break}Va=Va+1|0}y[g]=Va}}while(0);y[f+23]=-1;y[f+40]=1048575;y[f+3]=32-y[qa>>2]|0;w=1}}while(0);q=k;return w}Kc.X=1;function Lc(b){var c;if((b|0)!=0){c=y[b-4>>2];b=b-8|0;c=(c|0)==0?4:(c|0)==(y[b>>2]^-1|0)?5:4;c==4&&xc(M.p|0,645);Jc(b)}}function Mc(b){var c;if((b|0)!=0){c=y[b-4>>2];b=b-8|0;c=(c|0)==0?4:(c|0)==(y[b>>2]^-1|0)?5:4;c==4&&xc(M.p|0,645);Jc(b)}}function Nc(b){return(z[b|0]&255)<<8|z[b+1|0]&255}function Oc(b){return(z[b+1|0]&255)<<16|(z[b|0]&255)<<24|z[b+3|0]&255|(z[b+2|0]&255)<<8}function Pc(b){return z[b|0]&255}function Qc(b){return z[b+2|0]&255|(z[b|0]&255)<<16|(z[b+1|0]&255)<<8}function Rc(b,c){if(b==0&&c==0||b==9&&c==0){var d=4}else{if(b==1&&c==0||b==2&&c==0||b==7&&c==0||b==8&&c==0||b==3&&c==0||b==4&&c==0||b==5&&c==0||b==6&&c==0){d=8}else{xc(M.M|0,2664);d=0}}return d}function Sc(b,c){return(b|0)==0|c>>>0<74?0:(Nc(b)|0)!=18552?0:Nc(b+2|0)>>>0<74?0:Oc(b+6|0)>>>0>c>>>0?0:b}function Tc(b,c,d){var e=d>>2;if((b|0)==0|c>>>0<74|(d|0)==0){e=0}else{if((y[e]|0)!=40){e=0}else{b=Sc(b,c);if((b|0)==0){e=0}else{y[e+1]=Nc(b+12|0);y[e+2]=Nc(b+14|0);y[e+3]=Pc(b+16|0);y[e+4]=Pc(b+17|0);c=b+18|0;d=d+32|0;y[d>>2]=Pc(c);y[d+4>>2]=0;d=Pc(c);y[e+5]=(d|0)==0?8:(d|0)==9?8:16;y[e+6]=Oc(b+25|0);y[e+7]=Oc(b+29|0);e=1}}}return e}Tc.X=1;function Uc(b){y[b>>2]=0;var c=b+4|0;y[c>>2]=0;y[c+4>>2]=0;y[c+8>>2]=0;v[c+12|0]=0;y[b+20>>2]=0}function Vc(b){var c=y[b+20>>2];if((c|0)!=0&&(c|0)!=0){Wc(c);Jc(c)}Xc(b+4|0)}function Xc(b){var c=b|0,d=y[c>>2];if((d|0)!=0){var e=b+4|0;Jc(d);y[c>>2]=0;y[e>>2]=0;y[b+8>>2]=0}v[b+12|0]=0}function Yc(b,c){var d;d=(b+4|0)>>2;var e=C[d],g=(e|0)==(c|0);do{if(g){var h=1}else{if(e>>>0<=c>>>0){if(C[b+8>>2]>>>0<c>>>0){h=b;if(Fc(h,c,(e+1|0)==(c|0),1,0)){h=1}else{v[h+12|0]=1;h=0}if(!h){h=0;break}h=y[d]}else{h=e}bc(y[b>>2]+h|0,0,c-h|0)}y[d]=c;h=1}}while(0);return h}function Zc(b,c){C[b+4>>2]>>>0>c>>>0||xc(M.g|0,904);return y[b>>2]+c|0}function $c(b){var c=b+4|0,d=y[c+4>>2];(d|0)!=0&d>>>0<8193||xc(M.N|0,2998);var e=b|0;y[e>>2]=d;var g=b+20|0,h=C[g>>2];if((h|0)==0){d=Ic(180,0);if((d|0)==0){d=0}else{if((d|0)==0){d=0}else{y[d+164>>2]=0;y[d+168>>2]=0;y[d+172>>2]=0;y[d+176>>2]=0}}g=y[g>>2]=d;e=y[e>>2]}else{g=h;e=d}var c=Zc(c,0),b=C[b>>2],j;if(b>>>0>16){d=b>>>0>1;a:do{if(d){for(var i=0,h=b;;){i=i+1|0;if(h>>>0<=3){j=i;break a}h=h>>>1}}else{j=0}}while(0);j=(j|0)==32?32:(1<<j>>>0<b>>>0&1)+j|0;j=((j+1|0)>>>0<11?j+1|0:11)&255}else{j=0}return Kc(g,e,c,j)}function ad(b,c){if((c|0)==0){var d=0}else{if(c>>>0>16){var d=bd(b,c-16|0),e=bd(b,16),d=d<<16|e}else{d=bd(b,c)}}return d}function R(b,c){var d,e,g,h;g=C[c+20>>2]>>2;e=(b+20|0)>>2;var j=C[e];if((j|0)<24){d=(b+4|0)>>2;var i=C[d],f=C[b+8>>2];h=i>>>0<f>>>0;if((j|0)<16){if(h){h=i+1|0;i=(z[i]&255)<<8}else{h=i;i=0}if(h>>>0<f>>>0){f=h+1|0;h=z[h]&255}else{f=h;h=0}y[d]=f;y[e]=j+16|0;d=b+16|0;j=(h|i)<<16-j|y[d>>2]}else{if(h){y[d]=i+1|0;i=z[i]&255}else{i=0}y[e]=j+8|0;d=b+16|0;j=i<<24-j|y[d>>2]}y[d>>2]=j}else{j=y[b+16>>2]}d=b+16|0;i=(j>>>16)+1|0;f=i>>>0>C[g+4]>>>0;do{if(f){h=C[g+5];var k=h-1|0,o=i>>>0>C[((k<<2)+28>>2)+g]>>>0;a:do{if(o){for(var m=h;;){var t=m+1|0;if(i>>>0<=C[((m<<2)+28>>2)+g]>>>0){var s=t,w=m;break a}m=t}}else{s=h;w=k}}while(0);h=(j>>>((32-s|0)>>>0))+y[((w<<2)+96>>2)+g]|0;if(h>>>0<C[c>>2]>>>0){r=s;x=A[y[g+44]+(h<<1)>>1]&65535;h=22}else{xc(M.o|0,3267);var u=0;h=23}}else{r=C[y[g+42]+(j>>>((32-y[g+2]|0)>>>0)<<2)>>2];(r|0)==-1&&xc(M.R|0,3245);x=r&65535;r=r>>>16;h=c+4|0;k=x;C[h+4>>2]>>>0>k>>>0||xc(M.g|0,903);if((z[y[h>>2]+k|0]&255|0)==(r|0)){var r=r,x=x}else{xc(M.S|0,3249)}h=22}}while(0);if(h==22){y[d>>2]=y[d>>2]<<r;y[e]=y[e]-r|0;u=x}return u}R.X=1;function cd(b,c,d){if((d|0)==0){b=0}else{y[b>>2]=c;y[b+4>>2]=c;y[b+12>>2]=d;y[b+8>>2]=c+d|0;y[b+16>>2]=0;y[b+20>>2]=0;b=1}return b}function bd(b,c){var d;c>>>0<33||xc(M.P|0,3191);d=(b+20|0)>>2;var e=C[d],g=(e|0)<(c|0);a:do{if(g){for(var h=b+4|0,j=b+8|0,i=b+16|0,f=e;;){var k=y[h>>2];if((k|0)==(y[j>>2]|0)){k=0}else{y[h>>2]=k+1|0;k=z[k]&255}f=f+8|0;y[d]=f;if((f|0)>=33){xc(M.Q|0,3200);f=y[d]}k=k<<32-f|y[i>>2];y[i>>2]=k;if((f|0)>=(c|0)){var o=f,m=k;break a}}}else{o=e;m=y[b+16>>2]}}while(0);y[b+16>>2]=m<<c;y[d]=o-c|0;return m>>>((32-c|0)>>>0)}bd.X=1;function dd(b,c){var d,e=q;q=q+24;a:do{for(var g=0,h=8192;;){h=h>>>1;g=g+1|0;if((h|0)==0){d=g;break a}}}while(0);d=ad(b,d);g=(d|0)==0;do{if(g){h=c;y[h>>2]=0;Xc(h+4|0);var h=h+20|0,j=y[h>>2];if((j|0)!=0){if((j|0)!=0){Wc(j);Jc(j)}y[h>>2]=0}h=1}else{h=c+4|0;if(Yc(h,d)){j=Zc(h,0);bc(j,0,d);j=ad(b,5);if((j|0)==0|j>>>0>21){h=0}else{Uc(e);var i=e+4|0,f=Yc(i,21);a:do{if(f){for(var k=0;;){var o=ad(b,3),m=Zc(i,z[M.C+k|0]&255);v[m]=o&255;k=k+1|0;if((k|0)==(j|0)){break}}if($c(e)){k=0;b:for(;;){for(var m=k>>>0<d>>>0,o=d-k|0,t=(k|0)==0,s=k-1|0;;){if(!m){if((k|0)!=(d|0)){p=0;break a}p=$c(c);break a}var w=R(b,e);if(w>>>0<17){o=Zc(h,k);v[o]=w&255;k=k+1|0;continue b}if((w|0)==17){m=ad(b,3)+3|0;if(m>>>0>o>>>0){p=0;break a}k=m+k|0;continue b}else{if((w|0)==18){m=ad(b,7)+11|0;if(m>>>0>o>>>0){p=0;break a}k=m+k|0;continue b}else{if((w-19|0)>>>0>=2){xc(M.o|0,3141);p=0;break a}w=(w|0)==19?ad(b,2)+3|0:ad(b,6)+7|0;if(t|w>>>0>o>>>0){p=0;break a}var u=Zc(h,s),u=z[u];if(u<<24>>24==0){p=0;break a}var r=w+k|0;if(k>>>0<r>>>0){var x=k;break}}}}for(;;){o=Zc(h,x);m=x+1|0;v[o]=u;if((m|0)==(r|0)){k=r;continue b}x=m}}}else{var p=0}}else{p=0}}while(0);Vc(e);h=p}}else{h=0}}}while(0);q=e;return h}dd.X=1;function ed(b,c,d,e,g,h,j){var i=b+88|0,f=C[i>>2],k=((Nc(f+12|0)>>>(j>>>0)>>>0>1?Nc(f+12|0)>>>(j>>>0):1)+3|0)>>>2,j=((Nc(f+14|0)>>>(j>>>0)>>>0>1?Nc(f+14|0)>>>(j>>>0):1)+3|0)>>>2,f=Pc(f+18|0),f=((f|0)==0?8:(f|0)==9?8:16)*k|0;if((h|0)==0){var o=f,h=5}else{if(f>>>0<=h>>>0&(h&3|0)==0){o=h;h=5}else{var m=0,h=12}}if(h==5){if((o*j|0)>>>0>g>>>0){m=0}else{g=(k+1|0)>>>1;m=(j+1|0)>>>1;if(cd(b+92|0,c,d)){c=Pc(y[i>>2]+18|0);if((c|0)==0){fd(b,e,0,o,k,j,g,m);m=1}else{if((c|0)==2||(c|0)==3||(c|0)==5||(c|0)==6||(c|0)==4){gd(b,e,0,o,k,j,g,m);m=1}else{if((c|0)==9){hd(b,e,0,o,k,j,g,m);m=1}else{if((c|0)==7||(c|0)==8){id(b,e,0,o,k,j,g,m);m=1}else{m=0}}}}}else{m=0}}}return m}ed.X=1;Module._crn_get_width=(function(b,c){var d=q;q=q+40;jd(d);Tc(b,c,d);var e=y[d+4>>2];q=d;return e});Module._crn_get_height=(function(b,c){var d=q;q=q+40;jd(d);Tc(b,c,d);var e=y[d+8>>2];q=d;return e});Module._crn_get_levels=(function(b,c){var d=q;q=q+40;jd(d);Tc(b,c,d);var e=y[d+12>>2];q=d;return e});Module._crn_get_dxt_format=(function(b,c){var d=q;q=q+40;jd(d);Tc(b,c,d);var e=y[(d+32|0)>>2];q=d;return e});Module._crn_get_uncompressed_size=(function(b,c,d){var e=q;q=q+40;jd(e);Tc(b,c,e);b=((C[e+4>>2]>>>(d>>>0))+3|0)>>>2;d=((C[e+8>>2]>>>(d>>>0))+3|0)>>>2;c=e+32|0;c=Rc(y[c>>2],y[c+4>>2])<<1&536870910;q=e;return b*c*d|0});Module._crn_decompress=(function(b,c,d,e,g){var h=q;q=q+44;var j=h+40;jd(h);Tc(b,c,h);var i=((C[h+4>>2]>>>(g>>>0))+3|0)>>>2,f=h+32|0,f=Rc(y[f>>2],y[f+4>>2])<<1&536870910,i=i*f|0,k;if((b|0)==0|c>>>0<62){k=0}else{f=Ic(300,0);if((f|0)==0){f=0}else{if((f|0)==0){f=0}else{y[f>>2]=519686845;y[f+4>>2]=0;y[f+8>>2]=0;y[f+88>>2]=0;var o=(f+92|0)>>2;y[o]=0;y[o+1]=0;y[o+2]=0;y[o+3]=0;y[o+4]=0;y[o+5]=0;Uc(f+116|0);Uc(f+140|0);Uc(f+164|0);Uc(f+188|0);Uc(f+212|0);kd(f+236|0);kd(f+252|0);ld(f+268|0);ld(f+284|0)}}if((f|0)==0){k=0}else{o=Sc(b,c);y[f+88>>2]=o;if((o|0)==0){k=0}else{y[f+4>>2]=b;y[f+8>>2]=c;var c=f+92|0,o=y[f+4>>2],b=(f+88|0)>>2,m=y[b],o=cd(c,o+Qc(m+67|0)|0,Nc(m+65|0));do{if(o){if(dd(c,f+116|0)){m=y[b];if((Nc(m+39|0)|0)==0){if((Nc(m+55|0)|0)==0){m=0;break}}else{if(!dd(c,f+140|0)){m=0;break}if(!dd(c,f+188|0)){m=0;break}m=y[b]}if((Nc(m+55|0)|0)!=0){if(!dd(c,f+164|0)){m=0;break}if(!dd(c,f+212|0)){m=0;break}}m=1}else{m=0}}else{m=0}}while(0);if(m){b=f+88|0;c=y[b>>2];if((Nc(c+39|0)|0)==0){k=c;b=5}else{if(md(f)){if(nd(f)){k=y[b>>2];b=5}else{t=0;b=9}}else{var t=0,b=9}}do{if(b==5){if((Nc(k+55|0)|0)!=0){if(!od(f)){t=0;break}if(!pd(f)){t=0;break}}t=1}}while(0);k=t}else{k=0}}if(k){k=f}else{if((f|0)!=0){qd(f);Jc(f)}k=0}}}j=j|0;y[j>>2]=d;if(!((k|0)==0|(j|0)==0|e>>>0<8|g>>>0>15)&&(y[k>>2]|0)==519686845){t=C[k+88>>2];d=Oc((g<<2)+t+70|0);f=y[k+8>>2];b=g+1|0;t=b>>>0<Pc(t+16|0)>>>0?Oc((b<<2)+t+70|0):f;t>>>0>d>>>0||xc(M.U|0,3705);ed(k,y[k+4>>2]+d|0,t-d|0,j,e,i,g)}if((k|0)!=0&&(y[k>>2]|0)==519686845&&(k|0)!=0){qd(k);Jc(k)}q=h});function ld(b){y[b>>2]=0;y[b+4>>2]=0;y[b+8>>2]=0;v[b+12|0]=0}function kd(b){y[b>>2]=0;y[b+4>>2]=0;y[b+8>>2]=0;v[b+12|0]=0}function jd(b){y[b>>2]=40}function rd(b){var c=b|0,d=y[c>>2];if((d|0)!=0){var e=b+4|0;Jc(d);y[c>>2]=0;y[e>>2]=0;y[b+8>>2]=0}v[b+12|0]=0}function sd(b){var c=b|0,d=y[c>>2];if((d|0)!=0){var e=b+4|0;Jc(d);y[c>>2]=0;y[e>>2]=0;y[b+8>>2]=0}v[b+12|0]=0}function Wc(b){var c=y[b+168>>2];(c|0)!=0&&Mc(c);b=y[b+176>>2];(b|0)!=0&&Lc(b)}function fd(b,c,d,e,g,h,j,i){var f,k,o,m,t,s=q;q=q+24;t=s>>2;var w=s+4;m=w>>2;var d=s+8>>2,u=b+236|0,r=y[u+4>>2],x=b+252|0,p=y[x+4>>2];y[t]=0;y[m]=0;var B=Pc(y[b+88>>2]+17|0),I=e>>>2,S=(B|0)==0;a:do{if(!S){for(var Q=(i|0)==0,H=i-1|0,J=(h&1|0)!=0,K=e<<1,E=b+92|0,da=b+116|0,N=b+188|0,ka=I+1|0,ba=I+2|0,ra=I+3|0,ia=j-1|0,fa=b+140|0,Ga=ia<<4,ta=(g&1|0)!=0,la=0,Aa=1;;){b:do{if(Q){var xa=Aa}else{for(var Y=y[c+(la<<2)>>2],ma=0,ga=Aa;;){if((ma&1|0)==0){var ua=Y,Ba=16,Ca=1,va=j,Z=0}else{ua=Y+Ga|0;Ba=-16;va=Ca=-1;Z=ia}var sa=(ma|0)==(H|0),T=sa&J,ea=(Z|0)==(va|0);c:do{if(ea){var W=ga}else{var ya=sa&J^1,L=ga,O=ua;o=O>>2;for(var qa=Z;;){var na=(L|0)==1?R(E,da)|512:L,L=na&7,na=na>>>3;k=z[M.f+L|0]&255;for(var U=0,P=y[t];;){var F=R(E,fa);y[t]=P+F|0;td(s,r);P=C[t];F=ud(u,P);y[(U<<2>>2)+d]=y[F>>2];U=U+1|0;if(U>>>0>=k>>>0){break}}U=(qa|0)==(ia|0)&ta;k=O>>2;P=T|U;d:do{if(P){for(F=0;;){var oa=F*e|0;f=oa>>2;var wa=O+oa|0,ca=(F|0)==0|ya,V=F<<1,ja=R(E,N);y[m]=y[m]+ja|0;td(w,p);if(U){if(ca){y[wa>>2]=y[((z[(L<<2)+vd+V|0]&255)<<2>>2)+d];V=ud(x,y[m]);y[f+(o+1)]=y[V>>2]}f=R(E,N);y[m]=y[m]+f|0;td(w,p)}else{if(ca){y[wa>>2]=y[((z[(L<<2)+vd+V|0]&255)<<2>>2)+d];wa=ud(x,y[m]);y[f+(o+1)]=y[wa>>2];oa=oa+(O+8)|0;wa=R(E,N);y[m]=y[m]+wa|0;td(w,p);y[oa>>2]=y[((z[(L<<2)+vd+(V|1)|0]&255)<<2>>2)+d];V=ud(x,y[m]);y[f+(o+3)]=y[V>>2]}else{f=R(E,N);y[m]=y[m]+f|0;td(w,p)}}F=F+1|0;if((F|0)==2){break d}}}else{y[k]=y[((z[(L<<2)+vd|0]&255)<<2>>2)+d];F=R(E,N);y[m]=y[m]+F|0;td(w,p);F=ud(x,y[m]);y[o+1]=y[F>>2];y[o+2]=y[((z[(L<<2)+vd+1|0]&255)<<2>>2)+d];F=R(E,N);y[m]=y[m]+F|0;td(w,p);F=ud(x,y[m]);y[o+3]=y[F>>2];y[(I<<2>>2)+k]=y[((z[(L<<2)+vd+2|0]&255)<<2>>2)+d];F=R(E,N);y[m]=y[m]+F|0;td(w,p);F=ud(x,y[m]);y[(ka<<2>>2)+k]=y[F>>2];y[(ba<<2>>2)+k]=y[((z[(L<<2)+vd+3|0]&255)<<2>>2)+d];F=R(E,N);y[m]=y[m]+F|0;td(w,p);F=ud(x,y[m]);y[(ra<<2>>2)+k]=y[F>>2]}}while(0);qa=qa+Ca|0;if((qa|0)==(va|0)){W=na;break c}L=na;O=O+Ba|0;o=O>>2}}}while(0);o=ma+1|0;if((o|0)==(i|0)){xa=W;break b}Y=Y+K|0;ma=o;ga=W}}}while(0);la=la+1|0;if((la|0)==(B|0)){break a}Aa=xa}}}while(0);q=s;return 1}fd.X=1;function qd(b){y[b>>2]=0;sd(b+284|0);sd(b+268|0);rd(b+252|0);rd(b+236|0);var c=b+188|0;Vc(b+212|0);Vc(c);c=b+140|0;Vc(b+164|0);Vc(c);Vc(b+116|0)}qd.X=1;function td(b,c){var d=y[b>>2],e=d-c|0,g=e>>31;y[b>>2]=g&d|e&(g^-1)}function gd(b,c,d,e,g,h,j,i){var f,k,o,m,t,s,w,u,r=q;q=q+48;u=r>>2;var x=r+4;w=x>>2;var p=r+8;s=p>>2;var B=r+12;t=B>>2;m=r+16>>2;var d=r+32>>2,I=b+236|0,S=y[I+4>>2],Q=b+252|0,H=y[Q+4>>2],J=b+268|0,K=y[J+4>>2],E=y[b+88>>2],da=Nc(E+63|0);y[u]=0;y[w]=0;y[s]=0;y[t]=0;var E=Pc(E+17|0),N=(E|0)==0;a:do{if(!N){for(var ka=(i|0)==0,ba=i-1|0,ra=(h&1|0)==0,ia=e<<1,fa=b+92|0,Ga=b+116|0,ta=b+212|0,la=b+188|0,Aa=b+284|0,xa=b+140|0,Y=b+164|0,ma=j-1|0,ga=ma<<5,ua=(g&1|0)!=0,Ba=0,Ca=1;;){b:do{if(ka){var va=Ca}else{for(var Z=y[c+(Ba<<2)>>2],sa=0,T=Ca;;){if((sa&1|0)==0){var ea=Z,W=32,ya=1,L=j,O=0}else{ea=Z+ga|0;W=-32;L=ya=-1;O=ma}var qa=ra|(sa|0)!=(ba|0),na=(O|0)==(L|0);c:do{if(na){var U=T}else{for(var P=T,F=ea,oa=O;;){var wa=(P|0)==1?R(fa,Ga)|512:P,P=wa&7,wa=wa>>>3;o=z[M.f+P|0]&255;for(var ca=0,V=y[s];;){var ja=R(fa,Y);y[s]=V+ja|0;td(p,K);V=C[s];ja=wd(J,V);y[(ca<<2>>2)+d]=A[ja>>1]&65535;ca=ca+1|0;if(ca>>>0>=o>>>0){break}}ca=0;for(V=y[u];;){ja=R(fa,xa);y[u]=V+ja|0;td(r,S);V=C[u];ja=ud(I,V);y[(ca<<2>>2)+m]=y[ja>>2];ca=ca+1|0;if(ca>>>0>=o>>>0){break}}ca=(oa|0)==(ma|0)&ua;V=F;o=V>>2;for(ja=0;;){var Oa=(ja|0)==0|qa;f=ja<<1;k=R(fa,ta);y[t]=y[t]+k|0;td(B,da);k=R(fa,la);y[w]=y[w]+k|0;td(x,H);if(Oa){var hb=V,Ha=z[(P<<2)+vd+f|0]&255;k=wd(Aa,y[t]*3|0)>>1;y[hb>>2]=(A[k]&65535)<<16|y[(Ha<<2>>2)+d];y[o+1]=(A[k+2]&65535)<<16|A[k+1]&65535;y[o+2]=y[(Ha<<2>>2)+m];k=ud(Q,y[w]);y[o+3]=y[k>>2]}k=R(fa,ta);y[t]=y[t]+k|0;td(B,da);k=R(fa,la);y[w]=y[w]+k|0;td(x,H);if(!(ca|Oa^1)){Oa=V+16|0;k=z[(P<<2)+vd+(f|1)|0]&255;f=wd(Aa,y[t]*3|0)>>1;y[Oa>>2]=(A[f]&65535)<<16|y[(k<<2>>2)+d];y[o+5]=(A[f+2]&65535)<<16|A[f+1]&65535;y[o+6]=y[(k<<2>>2)+m];f=ud(Q,y[w]);y[o+7]=y[f>>2]}ja=ja+1|0;if((ja|0)==2){break}V=V+e|0;o=V>>2}oa=oa+ya|0;if((oa|0)==(L|0)){U=wa;break c}P=wa;F=F+W|0}}}while(0);sa=sa+1|0;if((sa|0)==(i|0)){va=U;break b}Z=Z+ia|0;T=U}}}while(0);Ba=Ba+1|0;if((Ba|0)==(E|0)){break a}Ca=va}}}while(0);q=r;return 1}gd.X=1;function hd(b,c,d,e,g,h,j,i){var f,k,o,m,t,s=q;q=q+24;t=s>>2;var w=s+4;m=w>>2;var d=s+8>>2,u=b+268|0,r=y[u+4>>2],x=y[b+88>>2],p=Nc(x+63|0);y[t]=0;y[m]=0;var x=Pc(x+17|0),B=(x|0)==0;a:do{if(!B){for(var I=(i|0)==0,S=i-1|0,Q=(h&1|0)==0,H=e<<1,J=b+92|0,K=b+116|0,E=(g&1|0)==0,da=b+164|0,N=b+212|0,ka=b+284|0,ba=j-1|0,ra=ba<<4,ia=0,fa=1;;){b:do{if(I){var Ga=fa}else{for(var ta=y[c+(ia<<2)>>2],la=0,Aa=fa;;){if((la&1|0)==0){var xa=ta,Y=16,ma=1,ga=j,ua=0}else{xa=ta+ra|0;Y=-16;ga=ma=-1;ua=ba}var Ba=Q|(la|0)!=(S|0),Ca=(ua|0)==(ga|0);c:do{if(Ca){var va=Aa}else{for(var Z=Aa,sa=xa,T=ua;;){var ea=(Z|0)==1?R(J,K)|512:Z,Z=ea&7,ea=ea>>>3,W=z[M.f+Z|0]&255,ya=E|(T|0)!=(ba|0);f=0;for(k=y[t];;){var L=R(J,da);y[t]=k+L|0;td(s,r);k=C[t];L=wd(u,k);y[(f<<2>>2)+d]=A[L>>1]&65535;f=f+1|0;if(f>>>0>=W>>>0){var O=sa;o=O>>2;var qa=0;break}}for(;;){W=O;k=(qa|0)==0|Ba;f=qa<<1;L=R(J,N);y[m]=y[m]+L|0;td(w,p);if(ya){if(k){L=z[(Z<<2)+vd+f|0]&255;k=wd(ka,y[m]*3|0)>>1;y[W>>2]=(A[k]&65535)<<16|y[(L<<2>>2)+d];y[o+1]=(A[k+2]&65535)<<16|A[k+1]&65535;W=O+8|0;k=R(J,N);y[m]=y[m]+k|0;td(w,p);k=z[(Z<<2)+vd+(f|1)|0]&255;f=wd(ka,y[m]*3|0)>>1;y[W>>2]=(A[f]&65535)<<16|y[(k<<2>>2)+d];y[o+3]=(A[f+2]&65535)<<16|A[f+1]&65535}else{W=R(J,N);y[m]=y[m]+W|0;td(w,p)}}else{if(k){k=z[(Z<<2)+vd+f|0]&255;f=wd(ka,y[m]*3|0)>>1;y[W>>2]=(A[f]&65535)<<16|y[(k<<2>>2)+d];y[o+1]=(A[f+2]&65535)<<16|A[f+1]&65535}W=R(J,N);y[m]=y[m]+W|0;td(w,p)}W=qa+1|0;if((W|0)==2){break}O=O+e|0;o=O>>2;qa=W}T=T+ma|0;if((T|0)==(ga|0)){va=ea;break c}Z=ea;sa=sa+Y|0}}}while(0);la=la+1|0;if((la|0)==(i|0)){Ga=va;break b}ta=ta+H|0;Aa=va}}}while(0);ia=ia+1|0;if((ia|0)==(x|0)){break a}fa=Ga}}}while(0);q=s;return 1}hd.X=1;function id(b,c,d,e,g,h,j,i){var f,k,o,m,t,s,w,u,r,x=q;q=q+48;r=x>>2;var p=x+4;u=p>>2;var B=x+8;w=B>>2;var I=x+12;s=I>>2;t=x+16>>2;var d=x+32>>2,S=b+268|0,Q=y[S+4>>2],H=y[b+88>>2],J=Nc(H+63|0);y[r]=0;y[u]=0;y[w]=0;y[s]=0;var H=Pc(H+17|0),K=(H|0)==0;a:do{if(!K){for(var E=(i|0)==0,da=i-1|0,N=(h&1|0)==0,ka=e<<1,ba=b+92|0,ra=b+116|0,ia=b+212|0,fa=b+284|0,Ga=b+164|0,ta=j-1|0,la=ta<<5,Aa=(g&1|0)!=0,xa=0,Y=1;;){b:do{if(E){var ma=Y}else{for(var ga=y[c+(xa<<2)>>2],ua=0,Ba=Y;;){if((ua&1|0)==0){var Ca=ga,va=32,Z=1,sa=j,T=0}else{Ca=ga+la|0;va=-32;sa=Z=-1;T=ta}var ea=N|(ua|0)!=(da|0),W=(T|0)==(sa|0);c:do{if(W){var ya=Ba}else{for(var L=Ba,O=Ca,qa=T;;){var na=(L|0)==1?R(ba,ra)|512:L,L=na&7,na=na>>>3;m=z[M.f+L|0]&255;for(var U=0,P=y[r];;){var F=R(ba,Ga);y[r]=P+F|0;td(x,Q);P=C[r];F=wd(S,P);y[(U<<2>>2)+t]=A[F>>1]&65535;U=U+1|0;if(U>>>0>=m>>>0){break}}U=0;for(P=y[w];;){F=R(ba,Ga);y[w]=P+F|0;td(B,Q);P=C[w];F=wd(S,P);y[(U<<2>>2)+d]=A[F>>1]&65535;U=U+1|0;if(U>>>0>=m>>>0){break}}U=(qa|0)==(ta|0)&Aa;P=O;m=P>>2;for(F=0;;){var oa=(F|0)==0|ea;f=F<<1;k=R(ba,ia);y[u]=y[u]+k|0;td(p,J);k=R(ba,ia);y[s]=y[s]+k|0;td(I,J);if(oa){var wa=P,ca=z[(L<<2)+vd+f|0]&255;o=wd(fa,y[u]*3|0)>>1;k=wd(fa,y[s]*3|0)>>1;y[wa>>2]=(A[o]&65535)<<16|y[(ca<<2>>2)+t];y[m+1]=(A[o+2]&65535)<<16|A[o+1]&65535;y[m+2]=(A[k]&65535)<<16|y[(ca<<2>>2)+d];y[m+3]=(A[k+2]&65535)<<16|A[k+1]&65535}k=R(ba,ia);y[u]=y[u]+k|0;td(p,J);k=R(ba,ia);y[s]=y[s]+k|0;td(I,J);if(!(U|oa^1)){oa=P+16|0;o=z[(L<<2)+vd+(f|1)|0]&255;k=wd(fa,y[u]*3|0)>>1;f=wd(fa,y[s]*3|0)>>1;y[oa>>2]=(A[k]&65535)<<16|y[(o<<2>>2)+t];y[m+5]=(A[k+2]&65535)<<16|A[k+1]&65535;y[m+6]=(A[f]&65535)<<16|y[(o<<2>>2)+d];y[m+7]=(A[f+2]&65535)<<16|A[f+1]&65535}F=F+1|0;if((F|0)==2){break}P=P+e|0;m=P>>2}qa=qa+Z|0;if((qa|0)==(sa|0)){ya=na;break c}L=na;O=O+va|0}}}while(0);ua=ua+1|0;if((ua|0)==(i|0)){ma=ya;break b}ga=ga+ka|0;Ba=ya}}}while(0);xa=xa+1|0;if((xa|0)==(H|0)){break a}Y=ma}}}while(0);q=x;return 1}id.X=1;function wd(b,c){C[b+4>>2]>>>0>c>>>0||xc(M.g|0,904);return(c<<1)+y[b>>2]|0}function ud(b,c){C[b+4>>2]>>>0>c>>>0||xc(M.g|0,904);return(c<<2)+y[b>>2]|0}function xd(b,c){var d;d=(b+4|0)>>2;var e=C[d],g=(e|0)==(c|0);do{if(g){var h=1}else{if(e>>>0<=c>>>0){if(C[b+8>>2]>>>0<c>>>0){h=b;if(Fc(h,c,(e+1|0)==(c|0),2,0)){h=1}else{v[h+12|0]=1;h=0}if(!h){h=0;break}h=y[d]}else{h=e}bc((h<<1)+y[b>>2]|0,0,(c-h|0)<<1)}y[d]=c;h=1}}while(0);return h}function yd(b,c){var d;d=(b+4|0)>>2;var e=C[d],g=(e|0)==(c|0);do{if(g){var h=1}else{if(e>>>0<=c>>>0){if(C[b+8>>2]>>>0<c>>>0){h=b;if(Fc(h,c,(e+1|0)==(c|0),4,0)){h=1}else{v[h+12|0]=1;h=0}if(!h){h=0;break}h=y[d]}else{h=e}bc((h<<2)+y[b>>2]|0,0,(c-h|0)<<2)}y[d]=c;h=1}}while(0);return h}function md(b){var c=q;q=q+48;var d,e=b+88|0,g=Nc(y[e>>2]+39|0),h=b+236|0,j=yd(h,g);do{if(j){var i=b+92|0,f=y[e>>2];if(cd(i,y[b+4>>2]+Qc(f+33|0)|0,Qc(f+36|0))){f=c|0;Uc(f);var k=c+24|0;Uc(k);for(var o=0;;){if(o>>>0>=2){d=9;break}if(!dd(i,c+o*24|0)){var m=0;d=11;break}o=o+1|0}a:do{if(d==9){var t=ud(h,0);if((g|0)==0){m=1}else{for(var s=o=0,w=0,u=0,r=0,x=0,p=0;;){var x=R(i,f)+x&31,r=R(i,k)+r&63,u=R(i,f)+u&31,B=R(i,f)+w|0,w=B&31,s=R(i,k)+s&63,o=R(i,f)+o&31;y[t>>2]=r<<5|x<<11|u|B<<27|s<<21|o<<16;p=p+1|0;if((p|0)==(g|0)){m=1;break a}t=t+4|0}}}}while(0);Vc(k);Vc(f);i=m}else{i=0}}else{i=0}}while(0);q=c;return i}md.X=1;function nd(b){var c=q;q=q+480;var d=c+24,e=c+220,g=c+416,h=y[b+88>>2],j=Nc(h+47|0),i=b+92|0;if(cd(i,y[b+4>>2]+Qc(h+41|0)|0,Qc(h+44|0))){Uc(c);h=dd(i,c);a:do{if(h){for(var f=-3,k=-3,o=0;;){y[d+(o<<2)>>2]=f;y[e+(o<<2)>>2]=k;var f=f+1|0,m=(f|0)>3,k=(m&1)+k|0,o=o+1|0;if((o|0)==49){break}f=m?-3:f}bc(g,0,64);k=b+252|0;if(yd(k,j)){var t=ud(k,0);if((j|0)==0){ba=1}else{for(var k=g|0,o=g+4|0,f=g+8|0,m=g+12|0,s=g+16|0,w=g+20|0,u=g+24|0,r=g+28|0,x=g+32|0,p=g+36|0,B=g+40|0,I=g+44|0,S=g+48|0,Q=g+52|0,H=g+56|0,J=g+60|0,K=0;;){for(var E=0;;){var da=R(i,c),N=E<<1,ka=(N<<2)+g|0;y[ka>>2]=y[ka>>2]+y[d+(da<<2)>>2]&3;N=((N|1)<<2)+g|0;y[N>>2]=y[N>>2]+y[e+(da<<2)>>2]&3;E=E+1|0;if((E|0)==8){break}}y[t>>2]=(z[M.b+y[o>>2]|0]&255)<<2|z[M.b+y[k>>2]|0]&255|(z[M.b+y[f>>2]|0]&255)<<4|(z[M.b+y[m>>2]|0]&255)<<6|(z[M.b+y[s>>2]|0]&255)<<8|(z[M.b+y[w>>2]|0]&255)<<10|(z[M.b+y[u>>2]|0]&255)<<12|(z[M.b+y[r>>2]|0]&255)<<14|(z[M.b+y[x>>2]|0]&255)<<16|(z[M.b+y[p>>2]|0]&255)<<18|(z[M.b+y[B>>2]|0]&255)<<20|(z[M.b+y[I>>2]|0]&255)<<22|(z[M.b+y[S>>2]|0]&255)<<24|(z[M.b+y[Q>>2]|0]&255)<<26|(z[M.b+y[H>>2]|0]&255)<<28|(z[M.b+y[J>>2]|0]&255)<<30;K=K+1|0;if((K|0)==(j|0)){ba=1;break a}t=t+4|0}}}else{var ba=0}}else{ba=0}}while(0);Vc(c);b=ba}else{b=0}q=c;return b}nd.X=1;function od(b){var c=q;q=q+24;var d=y[b+88>>2],e=Nc(d+55|0),g=b+92|0;if(cd(g,y[b+4>>2]+Qc(d+49|0)|0,Qc(d+52|0))){Uc(c);d=dd(g,c);a:do{if(d){var h=b+268|0;if(xd(h,e)){h=wd(h,0);if((e|0)==0){m=1}else{for(var j=0,i=0,f=0;;){var k=R(g,c),o=R(g,c),j=k+j&255,i=o+i&255;gb[h>>1]=(i<<8|j)&65535;f=f+1|0;if((f|0)==(e|0)){m=1;break a}h=h+2|0}}}else{var m=0}}else{m=0}}while(0);Vc(c);b=m}else{b=0}q=c;return b}od.X=1;function pd(b){var c,d=q;q=q+1888;var e=d+24,g=d+924,h=d+1824,j=y[b+88>>2],i=Nc(j+63|0),f=b+92|0;if(cd(f,y[b+4>>2]+Qc(j+57|0)|0,Qc(j+60|0))){Uc(d);j=dd(f,d);a:do{if(j){for(var k=-7,o=-7,m=0;;){y[e+(m<<2)>>2]=k;y[g+(m<<2)>>2]=o;var k=k+1|0,t=(k|0)>7,o=(t&1)+o|0,m=m+1|0;if((m|0)==225){break}k=t?-7:k}bc(h,0,64);o=b+284|0;if(xd(o,i*3|0)){c=wd(o,0);if((i|0)==0){ra=1}else{var o=h|0,m=h+4|0,k=h+8|0,t=h+12|0,s=h+16|0,w=h+20|0,u=h+24|0,r=h+28|0,x=h+32|0,p=h+36|0,B=h+40|0,I=h+44|0,S=h+48|0,Q=h+52|0,H=h+56|0,J=h+60|0,K=c;c=K>>1;for(var E=0;;){for(var da=0;;){var N=R(f,d),ka=da<<1,ba=(ka<<2)+h|0;y[ba>>2]=y[ba>>2]+y[e+(N<<2)>>2]&7;ka=((ka|1)<<2)+h|0;y[ka>>2]=y[ka>>2]+y[g+(N<<2)>>2]&7;da=da+1|0;if((da|0)==8){break}}gb[c]=(z[M.a+y[m>>2]|0]&255)<<3|z[M.a+y[o>>2]|0]&255|(z[M.a+y[k>>2]|0]&255)<<6|(z[M.a+y[t>>2]|0]&255)<<9|(z[M.a+y[s>>2]|0]&255)<<12|(z[M.a+y[w>>2]|0]&255)<<15;gb[c+1]=(z[M.a+y[u>>2]|0]&255)<<2|(z[M.a+y[w>>2]|0]&255)>>>1|(z[M.a+y[r>>2]|0]&255)<<5|(z[M.a+y[x>>2]|0]&255)<<8|(z[M.a+y[p>>2]|0]&255)<<11|(z[M.a+y[B>>2]|0]&255)<<14;gb[c+2]=(z[M.a+y[I>>2]|0]&255)<<1|(z[M.a+y[B>>2]|0]&255)>>>2|(z[M.a+y[S>>2]|0]&255)<<4|(z[M.a+y[Q>>2]|0]&255)<<7|(z[M.a+y[H>>2]|0]&255)<<10|(z[M.a+y[J>>2]|0]&255)<<13;E=E+1|0;if((E|0)==(i|0)){ra=1;break a}K=K+6|0;c=K>>1}}}else{var ra=0}}else{ra=0}}while(0);Vc(d);b=ra}else{b=0}q=d;return b}pd.X=1;function ac(b){if(b>>>0<245){var c=b>>>0<11?16:b+11&-8,d=c>>>3,b=C[X>>2],e=b>>>(d>>>0);if((e&3|0)!=0){var g=(e&1^1)+d|0,c=g<<1,d=(c<<2)+X+40|0,h=(c+2<<2)+X+40|0,e=C[h>>2],c=e+8|0,j=C[c>>2];if((d|0)==(j|0)){y[X>>2]=b&(1<<g^-1)}else{if(j>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[h>>2]=j;y[j+12>>2]=d}b=g<<3;y[e+4>>2]=b|3;b=e+(b|4)|0;y[b>>2]=y[b>>2]|1;g=c;b=38}else{if(c>>>0>C[X+8>>2]>>>0){if((e|0)!=0){var g=2<<d,g=e<<d&(g|-g),d=(g&-g)-1|0,g=d>>>12&16,e=d>>>(g>>>0),d=e>>>5&8,h=e>>>(d>>>0),e=h>>>2&4,j=h>>>(e>>>0),h=j>>>1&2,j=j>>>(h>>>0),i=j>>>1&1,d=(d|g|e|h|i)+(j>>>(i>>>0))|0,g=d<<1,h=(g<<2)+X+40|0,j=(g+2<<2)+X+40|0,e=C[j>>2],g=e+8|0,i=C[g>>2];if((h|0)==(i|0)){y[X>>2]=b&(1<<d^-1)}else{if(i>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[j>>2]=i;y[i+12>>2]=h}h=d<<3;b=h-c|0;y[e+4>>2]=c|3;d=e+c|0;y[e+(c|4)>>2]=b|1;y[e+h>>2]=b;i=C[X+8>>2];if((i|0)!=0){c=y[X+20>>2];h=i>>>2&1073741822;e=(h<<2)+X+40|0;j=C[X>>2];i=1<<(i>>>3);if((j&i|0)==0){y[X>>2]=j|i;j=e;h=(h+2<<2)+X+40|0}else{h=(h+2<<2)+X+40|0;j=C[h>>2];if(j>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}}y[h>>2]=c;y[j+12>>2]=c;y[(c+8|0)>>2]=j;y[(c+12|0)>>2]=e}y[X+8>>2]=b;y[X+20>>2]=d;b=38}else{if((y[X+4>>2]|0)==0){f=c;b=30}else{b=zd(c);if((b|0)==0){f=c;b=30}else{g=b;b=38}}}}else{var f=c,b=30}}}else{if(b>>>0>4294967231){f=-1;b=30}else{b=b+11&-8;if((y[X+4>>2]|0)==0){f=b;b=30}else{c=Ad(b);if((c|0)==0){f=b;b=30}else{g=c;b=38}}}}if(b==30){c=C[X+8>>2];if(f>>>0>c>>>0){b=C[X+12>>2];if(f>>>0<b>>>0){b=b-f|0;y[X+12>>2]=b;c=C[X+24>>2];y[X+24>>2]=c+f|0;y[f+(c+4)>>2]=b|1;y[c+4>>2]=f|3;g=c+8|0}else{g=Bd(f)}}else{g=c-f|0;b=C[X+20>>2];if(g>>>0>15){y[X+20>>2]=b+f|0;y[X+8>>2]=g;y[f+(b+4)>>2]=g|1;y[b+c>>2]=g;y[b+4>>2]=f|3}else{y[X+8>>2]=0;y[X+20>>2]=0;y[b+4>>2]=c|3;f=c+(b+4)|0;y[f>>2]=y[f>>2]|1}g=b+8|0}}return g}Module._malloc=ac;ac.X=1;function zd(b){var c,d,e=y[X+4>>2],g=(e&-e)-1|0,e=g>>>12&16,h=g>>>(e>>>0),g=h>>>5&8;d=h>>>(g>>>0);var h=d>>>2&4,j=d>>>(h>>>0);d=j>>>1&2;var j=j>>>(d>>>0),i=j>>>1&1,e=g=C[X+((g|e|h|d|i)+(j>>>(i>>>0))<<2)+304>>2];d=e>>2;g=(y[g+4>>2]&-8)-b|0;a:for(;;){for(h=e;;){j=y[h+16>>2];if((j|0)==0){h=y[h+20>>2];if((h|0)==0){break a}}else{h=j}j=(y[h+4>>2]&-8)-b|0;if(j>>>0<g>>>0){e=h;d=e>>2;g=j;continue a}}}var j=e,f=C[X+16>>2],i=j>>>0<f>>>0;do{if(!i){var k=j+b|0,h=k;if(j>>>0<k>>>0){var i=C[d+6],k=C[d+3],o=(k|0)==(e|0);do{if(o){c=e+20|0;var m=y[c>>2];if((m|0)==0){c=e+16|0;m=y[c>>2];if((m|0)==0){m=0;c=m>>2;break}}for(;;){var t=m+20|0,s=y[t>>2];if((s|0)==0){t=m+16|0;s=C[t>>2];if((s|0)==0){break}}c=t;m=s}if(c>>>0<f>>>0){$();a("Reached an unreachable!")}y[c>>2]=0}else{c=C[d+2];if(c>>>0<f>>>0){$();a("Reached an unreachable!")}y[c+12>>2]=k;y[k+8>>2]=c;m=k}c=m>>2}while(0);f=(i|0)==0;a:do{if(!f){k=e+28|0;o=(y[k>>2]<<2)+X+304|0;t=(e|0)==(y[o>>2]|0);do{if(t){y[o>>2]=m;if((m|0)!=0){break}y[X+4>>2]=y[X+4>>2]&(1<<y[k>>2]^-1);break a}if(i>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}s=i+16|0;(y[s>>2]|0)==(e|0)?y[s>>2]=m:y[i+20>>2]=m;if((m|0)==0){break a}}while(0);if(m>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+6]=i;k=C[d+4];if((k|0)!=0){if(k>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+4]=k;y[k+24>>2]=m}k=C[d+5];if((k|0)!=0){if(k>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+5]=k;y[k+24>>2]=m}}}while(0);if(g>>>0<16){b=g+b|0;y[d+1]=b|3;b=b+(j+4)|0;y[b>>2]=y[b>>2]|1}else{y[d+1]=b|3;y[b+(j+4)>>2]=g|1;y[j+g+b>>2]=g;f=C[X+8>>2];if((f|0)!=0){b=C[X+20>>2];j=f>>>2&1073741822;d=(j<<2)+X+40|0;i=C[X>>2];f=1<<(f>>>3);if((i&f|0)==0){y[X>>2]=i|f;i=d;j=(j+2<<2)+X+40|0}else{j=(j+2<<2)+X+40|0;i=C[j>>2];if(i>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}}y[j>>2]=b;y[i+12>>2]=b;y[b+8>>2]=i;y[b+12>>2]=d}y[X+8>>2]=g;y[X+20>>2]=h}return e+8|0}}}while(0);$();a("Reached an unreachable!")}zd.X=1;function Bd(b){var c,d;(y[Cd>>2]|0)==0&&Dd();var e=(y[X+440>>2]&4|0)==0;do{if(e){d=y[X+24>>2];if((d|0)==0){d=6}else{d=Ed(d);if((d|0)==0){d=6}else{var g=y[Cd+8>>2],g=b+47-y[X+12>>2]+g&-g;if(g>>>0<2147483647){var h=Fd(g);if((h|0)==(y[d>>2]+y[d+4>>2]|0)){var j=h,i=g;c=h;d=13}else{var f=h,k=g;d=15}}else{d=14}}}if(d==6){d=Fd(0);if((d|0)==-1){d=14}else{var g=y[Cd+8>>2],g=g+(b+47)&-g,h=d,o=y[Cd+4>>2],m=o-1|0,g=(m&h|0)==0?g:g-h+(m+h&-o)|0;if(g>>>0<2147483647){h=Fd(g);if((h|0)==(d|0)){j=d;i=g;c=h;d=13}else{f=h;k=g;d=15}}else{d=14}}}if(d==13){if((j|0)!=-1){var t=i,s=j;d=26;break}f=c;k=i}else{if(d==14){y[X+440>>2]=y[X+440>>2]|4;d=23;break}}d=-k|0;if((f|0)!=-1&k>>>0<2147483647){if(k>>>0<(b+48|0)>>>0){g=y[Cd+8>>2];g=b+47-k+g&-g;if(g>>>0<2147483647){if((Fd(g)|0)==-1){Fd(d);d=22}else{w=g+k|0;d=21}}else{w=k;d=21}}else{var w=k;d=21}}else{w=k;d=21}if(d==21&&(f|0)!=-1){t=w;s=f;d=26;break}y[X+440>>2]=y[X+440>>2]|4}d=23}while(0);if(d==23){e=y[Cd+8>>2];e=e+(b+47)&-e;if(e>>>0<2147483647){e=Fd(e);j=Fd(0);if((j|0)!=-1&(e|0)!=-1&e>>>0<j>>>0){j=j-e|0;if(j>>>0<=(b+40|0)>>>0|(e|0)==-1){d=49}else{t=j;s=e;d=26}}else{d=49}}else{d=49}}a:do{if(d==26){e=y[X+432>>2]+t|0;y[X+432>>2]=e;e>>>0>C[X+436>>2]>>>0&&(y[X+436>>2]=e);e=C[X+24>>2];j=(e|0)==0;b:do{if(j){i=C[X+16>>2];(i|0)==0|s>>>0<i>>>0&&(y[X+16>>2]=s);y[X+444>>2]=s;y[X+448>>2]=t;y[X+456>>2]=0;y[X+36>>2]=y[Cd>>2];y[X+32>>2]=-1;for(i=0;;){c=i<<1;f=(c<<2)+X+40|0;y[X+(c+3<<2)+40>>2]=f;y[X+(c+2<<2)+40>>2]=f;i=i+1|0;if((i|0)==32){break}}Gd(s,t-40|0)}else{f=X+444|0;for(c=f>>2;;){if((f|0)==0){break}i=C[c];f=f+4|0;k=C[f>>2];w=i+k|0;if((s|0)==(w|0)){if((y[c+3]&8|0)!=0){break}c=e;if(!(c>>>0>=i>>>0&c>>>0<w>>>0)){break}y[f>>2]=k+t|0;Gd(y[X+24>>2],y[X+12>>2]+t|0);break b}f=y[c+2];c=f>>2}s>>>0<C[X+16>>2]>>>0&&(y[X+16>>2]=s);c=s+t|0;for(f=X+444|0;;){if((f|0)==0){break}k=f|0;i=C[k>>2];if((i|0)==(c|0)){if((y[f+12>>2]&8|0)!=0){break}y[k>>2]=s;var u=f+4|0;y[u>>2]=y[u>>2]+t|0;u=Hd(s,i,b);d=50;break a}f=y[f+8>>2]}Id(s,t)}}while(0);e=C[X+12>>2];if(e>>>0>b>>>0){u=e-b|0;y[X+12>>2]=u;j=e=C[X+24>>2];y[X+24>>2]=j+b|0;y[b+(j+4)>>2]=u|1;y[e+4>>2]=b|3;u=e+8|0;d=50}else{d=49}}}while(0);if(d==49){y[Jd>>2]=12;u=0}return u}Bd.X=1;function Ad(b){var c,d,e,g,h,j=b>>2,i=-b|0,f=b>>>8;if((f|0)==0){var k=0}else{if(b>>>0>16777215){k=31}else{var o=(f+1048320|0)>>>16&8,m=f<<o,t=(m+520192|0)>>>16&4,s=m<<t,w=(s+245760|0)>>>16&2,u=14-(t|o|w)+(s<<w>>>15)|0,k=b>>>((u+7|0)>>>0)&1|u<<1}}var r=C[X+(k<<2)+304>>2],x=(r|0)==0;a:do{if(x){var p=0,B=i,I=0}else{var S=(k|0)==31?0:25-(k>>>1)|0,Q=0,H=i,J=r;h=J>>2;for(var K=b<<S,E=0;;){var da=y[h+1]&-8,N=da-b|0;if(N>>>0<H>>>0){if((da|0)==(b|0)){p=J;B=N;I=J;break a}var ka=J,ba=N}else{ka=Q;ba=H}var ra=C[h+5],ia=C[((K>>>31<<2)+16>>2)+h],fa=(ra|0)==0|(ra|0)==(ia|0)?E:ra;if((ia|0)==0){p=ka;B=ba;I=fa;break a}Q=ka;H=ba;J=ia;h=J>>2;K=K<<1;E=fa}}}while(0);if((I|0)==0&(p|0)==0){var Ga=2<<k,ta=y[X+4>>2]&(Ga|-Ga);if((ta|0)==0){var la=I}else{var Aa=(ta&-ta)-1|0,xa=Aa>>>12&16,Y=Aa>>>(xa>>>0),ma=Y>>>5&8,ga=Y>>>(ma>>>0),ua=ga>>>2&4,Ba=ga>>>(ua>>>0),Ca=Ba>>>1&2,va=Ba>>>(Ca>>>0),Z=va>>>1&1,la=y[X+((ma|xa|ua|Ca|Z)+(va>>>(Z>>>0))<<2)+304>>2]}}else{la=I}var sa=(la|0)==0;a:do{if(sa){var T=B,ea=p;g=ea>>2}else{var W=la;e=W>>2;for(var ya=B,L=p;;){var O=(y[e+1]&-8)-b|0,qa=O>>>0<ya>>>0,na=qa?O:ya,U=qa?W:L,P=C[e+4];if((P|0)!=0){W=P}else{var F=C[e+5];if((F|0)==0){T=na;ea=U;g=ea>>2;break a}W=F}e=W>>2;ya=na;L=U}}}while(0);var oa=(ea|0)==0;a:do{if(oa){var wa=0}else{if(T>>>0<(y[X+8>>2]-b|0)>>>0){var ca=ea;d=ca>>2;var V=C[X+16>>2],ja=ca>>>0<V>>>0;do{if(!ja){var Oa=ca+b|0,hb=Oa;if(ca>>>0<Oa>>>0){var Ha=C[g+6],Ra=C[g+3],Za=(Ra|0)==(ea|0);do{if(Za){var Ob=ea+20|0,xb=y[Ob>>2];if((xb|0)==0){var Pb=ea+16|0,yb=y[Pb>>2];if((yb|0)==0){var ha=0;c=ha>>2;break}var Ka=Pb,La=yb}else{Ka=Ob;La=xb}for(;;){var ib=La+20|0,zb=y[ib>>2];if((zb|0)!=0){Ka=ib;La=zb}else{var Qb=La+16|0,Rb=C[Qb>>2];if((Rb|0)==0){break}Ka=Qb;La=Rb}}if(Ka>>>0<V>>>0){$();a("Reached an unreachable!")}y[Ka>>2]=0;ha=La}else{var $a=C[g+2];if($a>>>0<V>>>0){$();a("Reached an unreachable!")}y[$a+12>>2]=Ra;y[Ra+8>>2]=$a;ha=Ra}c=ha>>2}while(0);var Sb=(Ha|0)==0;b:do{if(!Sb){var Tb=ea+28|0,cc=(y[Tb>>2]<<2)+X+304|0,Ab=(ea|0)==(y[cc>>2]|0);do{if(Ab){y[cc>>2]=ha;if((ha|0)!=0){break}y[X+4>>2]=y[X+4>>2]&(1<<y[Tb>>2]^-1);break b}if(Ha>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}var bb=Ha+16|0;(y[bb>>2]|0)==(ea|0)?y[bb>>2]=ha:y[Ha+20>>2]=ha;if((ha|0)==0){break b}}while(0);if(ha>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+6]=Ha;var ab=C[g+4];if((ab|0)!=0){if(ab>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+4]=ab;y[ab+24>>2]=ha}var Ia=C[g+5];if((Ia|0)!=0){if(Ia>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+5]=Ia;y[Ia+24>>2]=ha}}}while(0);var Bb=T>>>0<16;b:do{if(Bb){var Cb=T+b|0;y[g+1]=Cb|3;var Db=Cb+(ca+4)|0;y[Db>>2]=y[Db>>2]|1}else{y[g+1]=b|3;y[j+(d+1)]=T|1;y[(T>>2)+d+j]=T;if(T>>>0<256){var ob=T>>>2&1073741822,pb=(ob<<2)+X+40|0,Eb=C[X>>2],Fb=1<<(T>>>3);if((Eb&Fb|0)==0){y[X>>2]=Eb|Fb;var qb=pb,jb=(ob+2<<2)+X+40|0}else{var rb=(ob+2<<2)+X+40|0,Sa=C[rb>>2];if(Sa>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}qb=Sa;jb=rb}y[jb>>2]=hb;y[qb+12>>2]=hb;y[j+(d+2)]=qb;y[j+(d+3)]=pb}else{var Pa=Oa,sb=T>>>8;if((sb|0)==0){var Ta=0}else{if(T>>>0>16777215){Ta=31}else{var Gb=(sb+1048320|0)>>>16&8,Hb=sb<<Gb,Ib=(Hb+520192|0)>>>16&4,tb=Hb<<Ib,Jb=(tb+245760|0)>>>16&2,Kb=14-(Ib|Gb|Jb)+(tb<<Jb>>>15)|0,Ta=T>>>((Kb+7|0)>>>0)&1|Kb<<1}}var Lb=(Ta<<2)+X+304|0;y[j+(d+7)]=Ta;var kb=b+(ca+16)|0;y[j+(d+5)]=0;y[kb>>2]=0;var Ua=y[X+4>>2],Ub=1<<Ta;if((Ua&Ub|0)==0){y[X+4>>2]=Ua|Ub;y[Lb>>2]=Pa;y[j+(d+6)]=Lb;y[j+(d+3)]=Pa;y[j+(d+2)]=Pa}else{for(var Va=T<<((Ta|0)==31?0:25-(Ta>>>1)|0),nb=y[Lb>>2];;){if((y[nb+4>>2]&-8|0)==(T|0)){var Ac=nb+8|0,Xb=C[Ac>>2],oc=C[X+16>>2],Bc=nb>>>0<oc>>>0;do{if(!Bc&&Xb>>>0>=oc>>>0){y[Xb+12>>2]=Pa;y[Ac>>2]=Pa;y[j+(d+2)]=Xb;y[j+(d+3)]=nb;y[j+(d+6)]=0;break b}}while(0);$();a("Reached an unreachable!")}var gc=(Va>>>31<<2)+nb+16|0,Cc=C[gc>>2];if((Cc|0)==0){if(gc>>>0>=C[X+16>>2]>>>0){y[gc>>2]=Pa;y[j+(d+6)]=nb;y[j+(d+3)]=Pa;y[j+(d+2)]=Pa;break b}$();a("Reached an unreachable!")}Va=Va<<1;nb=Cc}}}}}while(0);wa=ea+8|0;break a}}}while(0);$();a("Reached an unreachable!")}wa=0}}while(0);return wa}Ad.X=1;function Kd(b){var c;(y[Cd>>2]|0)==0&&Dd();var d=b>>>0<4294967232;a:do{if(d){var e=C[X+24>>2];if((e|0)!=0){var g=C[X+12>>2],h=g>>>0>(b+40|0)>>>0;do{if(h){var j=C[Cd+8>>2],i=(Math.floor(((-40-b-1+g+j|0)>>>0)/(j>>>0))-1)*j|0,f=Ed(e);if((y[f+12>>2]&8|0)==0){var k=Fd(0);c=(f+4|0)>>2;if((k|0)==(y[f>>2]+y[c]|0)){i=Fd(-(i>>>0>2147483646?-2147483648-j|0:i)|0);j=Fd(0);if((i|0)!=-1&j>>>0<k>>>0){i=k-j|0;if((k|0)!=(j|0)){y[c]=y[c]-i|0;y[X+432>>2]=y[X+432>>2]-i|0;Gd(y[X+24>>2],y[X+12>>2]-i|0);c=(k|0)!=(j|0);break a}}}}}}while(0);C[X+12>>2]>>>0>C[X+28>>2]>>>0&&(y[X+28>>2]=-1)}}c=0}while(0);return c&1}Kd.X=1;function Ld(b){var c,d,e,g,h,j,i=b>>2,f,k=(b|0)==0;a:do{if(!k){var o=b-8|0,m=o,t=C[X+16>>2],s=o>>>0<t>>>0;b:do{if(!s){var w=C[b-4>>2],u=w&3;if((u|0)!=1){var r=w&-8;j=r>>2;var x=b+(r-8)|0,p=x,B=(w&1|0)==0;c:do{if(B){var I=C[o>>2];if((u|0)==0){break a}var S=-8-I|0;h=S>>2;var Q=b+S|0,H=Q,J=I+r|0;if(Q>>>0<t>>>0){break b}if((H|0)==(y[X+20>>2]|0)){g=(b+(r-4)|0)>>2;if((y[g]&3|0)!=3){var K=H;e=K>>2;var E=J;break}y[X+8>>2]=J;y[g]=y[g]&-2;y[h+(i+1)]=J|1;y[x>>2]=J;break a}if(I>>>0<256){var da=C[h+(i+2)],N=C[h+(i+3)];if((da|0)==(N|0)){y[X>>2]=y[X>>2]&(1<<(I>>>3)^-1);K=H;e=K>>2;E=J}else{var ka=((I>>>2&1073741822)<<2)+X+40|0,ba=(da|0)!=(ka|0)&da>>>0<t>>>0;do{if(!ba&&(N|0)==(ka|0)|N>>>0>=t>>>0){y[da+12>>2]=N;y[N+8>>2]=da;K=H;e=K>>2;E=J;break c}}while(0);$();a("Reached an unreachable!")}}else{var ra=Q,ia=C[h+(i+6)],fa=C[h+(i+3)],Ga=(fa|0)==(ra|0);do{if(Ga){var ta=S+(b+20)|0,la=y[ta>>2];if((la|0)==0){var Aa=S+(b+16)|0,xa=y[Aa>>2];if((xa|0)==0){var Y=0;d=Y>>2;break}var ma=Aa,ga=xa}else{ma=ta;ga=la;f=21}for(;;){var ua=ga+20|0,Ba=y[ua>>2];if((Ba|0)!=0){ma=ua;ga=Ba}else{var Ca=ga+16|0,va=C[Ca>>2];if((va|0)==0){break}ma=Ca;ga=va}}if(ma>>>0<t>>>0){$();a("Reached an unreachable!")}y[ma>>2]=0;Y=ga}else{var Z=C[h+(i+2)];if(Z>>>0<t>>>0){$();a("Reached an unreachable!")}y[Z+12>>2]=fa;y[fa+8>>2]=Z;Y=fa}d=Y>>2}while(0);if((ia|0)!=0){var sa=S+(b+28)|0,T=(y[sa>>2]<<2)+X+304|0,ea=(ra|0)==(y[T>>2]|0);do{if(ea){y[T>>2]=Y;if((Y|0)!=0){break}y[X+4>>2]=y[X+4>>2]&(1<<y[sa>>2]^-1);K=H;e=K>>2;E=J;break c}if(ia>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}var W=ia+16|0;(y[W>>2]|0)==(ra|0)?y[W>>2]=Y:y[ia+20>>2]=Y;if((Y|0)==0){K=H;e=K>>2;E=J;break c}}while(0);if(Y>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[d+6]=ia;var ya=C[h+(i+4)];if((ya|0)!=0){if(ya>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[d+4]=ya;y[ya+24>>2]=Y}var L=C[h+(i+5)];if((L|0)!=0){if(L>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[d+5]=L;y[L+24>>2]=Y}}K=H;e=K>>2;E=J}}else{K=m;e=K>>2;E=r}}while(0);var O=K;if(O>>>0<x>>>0){var qa=b+(r-4)|0,na=C[qa>>2];if((na&1|0)!=0){var U=(na&2|0)==0;do{if(U){if((p|0)==(y[X+24>>2]|0)){var P=y[X+12>>2]+E|0;y[X+12>>2]=P;y[X+24>>2]=K;y[e+1]=P|1;if((K|0)==(y[X+20>>2]|0)){y[X+20>>2]=0;y[X+8>>2]=0}if(P>>>0<=C[X+28>>2]>>>0){break a}Kd(0);break a}if((p|0)==(y[X+20>>2]|0)){var F=y[X+8>>2]+E|0;y[X+8>>2]=F;y[X+20>>2]=K;y[e+1]=F|1;y[(O+F|0)>>2]=F;break a}var oa=(na&-8)+E|0,wa=na>>>3,ca=na>>>0<256;c:do{if(ca){var V=C[i+j],ja=C[((r|4)>>2)+i];if((V|0)==(ja|0)){y[X>>2]=y[X>>2]&(1<<wa^-1)}else{var Oa=((na>>>2&1073741822)<<2)+X+40|0;f=(V|0)==(Oa|0)?63:V>>>0<C[X+16>>2]>>>0?66:63;do{if(f==63&&!((ja|0)!=(Oa|0)&&ja>>>0<C[X+16>>2]>>>0)){y[V+12>>2]=ja;y[ja+8>>2]=V;break c}}while(0);$();a("Reached an unreachable!")}}else{var hb=x,Ha=C[j+(i+4)],Ra=C[((r|4)>>2)+i],Za=(Ra|0)==(hb|0);do{if(Za){var Ob=r+(b+12)|0,xb=y[Ob>>2];if((xb|0)==0){var Pb=r+(b+8)|0,yb=y[Pb>>2];if((yb|0)==0){var ha=0;c=ha>>2;break}var Ka=Pb,La=yb}else{Ka=Ob;La=xb;f=73}for(;;){var ib=La+20|0,zb=y[ib>>2];if((zb|0)!=0){Ka=ib;La=zb}else{var Qb=La+16|0,Rb=C[Qb>>2];if((Rb|0)==0){break}Ka=Qb;La=Rb}}if(Ka>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[Ka>>2]=0;ha=La}else{var $a=C[i+j];if($a>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[$a+12>>2]=Ra;y[Ra+8>>2]=$a;ha=Ra}c=ha>>2}while(0);if((Ha|0)!=0){var Sb=r+(b+20)|0,Tb=(y[Sb>>2]<<2)+X+304|0,cc=(hb|0)==(y[Tb>>2]|0);do{if(cc){y[Tb>>2]=ha;if((ha|0)!=0){break}y[X+4>>2]=y[X+4>>2]&(1<<y[Sb>>2]^-1);break c}if(Ha>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}var Ab=Ha+16|0;(y[Ab>>2]|0)==(hb|0)?y[Ab>>2]=ha:y[Ha+20>>2]=ha;if((ha|0)==0){break c}}while(0);if(ha>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+6]=Ha;var bb=C[j+(i+2)];if((bb|0)!=0){if(bb>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+4]=bb;y[bb+24>>2]=ha}var ab=C[j+(i+3)];if((ab|0)!=0){if(ab>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[c+5]=ab;y[ab+24>>2]=ha}}}}while(0);y[e+1]=oa|1;y[O+oa>>2]=oa;if((K|0)!=(y[X+20>>2]|0)){var Ia=oa}else{y[X+8>>2]=oa;break a}}else{y[qa>>2]=na&-2;y[e+1]=E|1;Ia=y[O+E>>2]=E}}while(0);if(Ia>>>0<256){var Bb=Ia>>>2&1073741822,Cb=(Bb<<2)+X+40|0,Db=C[X>>2],ob=1<<(Ia>>>3);if((Db&ob|0)==0){y[X>>2]=Db|ob;var pb=Cb,Eb=(Bb+2<<2)+X+40|0}else{var Fb=(Bb+2<<2)+X+40|0,qb=C[Fb>>2];if(qb>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}pb=qb;Eb=Fb}y[Eb>>2]=K;y[pb+12>>2]=K;y[e+2]=pb;y[e+3]=Cb;break a}var jb=K,rb=Ia>>>8;if((rb|0)==0){var Sa=0}else{if(Ia>>>0>16777215){Sa=31}else{var Pa=(rb+1048320|0)>>>16&8,sb=rb<<Pa,Ta=(sb+520192|0)>>>16&4,Gb=sb<<Ta,Hb=(Gb+245760|0)>>>16&2,Ib=14-(Ta|Pa|Hb)+(Gb<<Hb>>>15)|0,Sa=Ia>>>((Ib+7|0)>>>0)&1|Ib<<1}}var tb=(Sa<<2)+X+304|0;y[e+7]=Sa;y[e+5]=0;y[e+4]=0;var Jb=y[X+4>>2],Kb=1<<Sa,Lb=(Jb&Kb|0)==0;c:do{if(Lb){y[X+4>>2]=Jb|Kb;y[tb>>2]=jb;y[e+6]=tb;y[e+3]=K;y[e+2]=K}else{for(var kb=Ia<<((Sa|0)==31?0:25-(Sa>>>1)|0),Ua=y[tb>>2];;){if((y[Ua+4>>2]&-8|0)==(Ia|0)){var Ub=Ua+8|0,Va=C[Ub>>2],nb=C[X+16>>2],Ac=Ua>>>0<nb>>>0;do{if(!Ac&&Va>>>0>=nb>>>0){y[Va+12>>2]=jb;y[Ub>>2]=jb;y[e+2]=Va;y[e+3]=Ua;y[e+6]=0;break c}}while(0);$();a("Reached an unreachable!")}var Xb=(kb>>>31<<2)+Ua+16|0,oc=C[Xb>>2];if((oc|0)==0){if(Xb>>>0>=C[X+16>>2]>>>0){y[Xb>>2]=jb;y[e+6]=Ua;y[e+3]=K;y[e+2]=K;break c}$();a("Reached an unreachable!")}kb=kb<<1;Ua=oc}}}while(0);var Bc=y[X+32>>2]-1|0;y[X+32>>2]=Bc;if((Bc|0)!=0){break a}var gc=y[X+452>>2],Cc=(gc|0)==0;c:do{if(!Cc){for(var Td=gc;;){var Ud=y[Td+8>>2];if((Ud|0)==0){break c}Td=Ud}}}while(0);y[X+32>>2]=-1;break a}}}}}while(0);$();a("Reached an unreachable!")}}while(0)}Module._free=Ld;Ld.X=1;function Md(b,c){var d,e,g,h=c>>>0>4294967231;a:do{if(h){y[Jd>>2]=12;var j=0}else{g=d=b-8|0;e=(b-4|0)>>2;var i=C[e],f=i&-8,k=f-8|0,o=b+k|0,m=d>>>0<C[X+16>>2]>>>0;do{if(!m){var t=i&3;if((t|0)!=1&(k|0)>-8){d=(b+(f-4)|0)>>2;if((y[d]&1|0)!=0){h=c>>>0<11?16:c+11&-8;if((t|0)==0){var s=0,w,i=y[g+4>>2]&-8;w=h>>>0<256?0:i>>>0>=(h+4|0)>>>0&&(i-h|0)>>>0<=y[Cd+8>>2]<<1>>>0?g:0;g=17}else{if(f>>>0<h>>>0){if((o|0)!=(y[X+24>>2]|0)){g=21}else{d=y[X+12>>2]+f|0;if(d>>>0>h>>>0){s=d-h|0;w=b+(h-8)|0;y[e]=h|i&1|2;y[b+(h-4)>>2]=s|1;y[X+24>>2]=w;y[X+12>>2]=s;s=0;w=g;g=17}else{g=21}}}else{s=f-h|0;if(s>>>0>15){y[e]=h|i&1|2;y[b+(h-4)>>2]=s|3;y[d]=y[d]|1;s=b+h|0}else{s=0}w=g;g=17}}do{if(g==17&&(w|0)!=0){(s|0)!=0&&Ld(s);j=w+8|0;break a}}while(0);g=ac(c);if((g|0)==0){j=0;break a}e=f-((y[e]&3|0)==0?8:4)|0;f=g;i=b;e=e>>>0<c>>>0?e:c;if(e>=20&&i%2==f%2){if(i%4==f%4){for(e=i+e;i%4;){v[f++]=v[i++]}i=i>>2;f=f>>2;for(s=e>>2;i<s;){y[f++]=y[i++]}i=i<<2;for(f=f<<2;i<e;){v[f++]=v[i++]}}else{e=i+e;i%2&&(v[f++]=v[i++]);i=i>>1;f=f>>1;for(s=e>>1;i<s;){gb[f++]=gb[i++]}i=i<<1;f=f<<1;i<e&&(v[f++]=v[i++])}}else{for(;e--;){v[f++]=v[i++]}}Ld(b);j=g;break a}}}}while(0);$();a("Reached an unreachable!")}}while(0);return j}Md.X=1;function Dd(){if((y[Cd>>2]|0)==0){var b=Nd();if((b-1&b|0)==0){y[Cd+8>>2]=b;y[Cd+4>>2]=b;y[Cd+12>>2]=-1;y[Cd+16>>2]=2097152;y[Cd+20>>2]=0;y[X+440>>2]=0;y[Cd>>2]=Math.floor(Date.now()/1e3)&-16^1431655768}else{$();a("Reached an unreachable!")}}}function Od(b){if((b|0)==0){b=0}else{var b=y[b-4>>2],c=b&3,b=(c|0)==1?0:(b&-8)-((c|0)==0?8:4)|0}return b}function Ed(b){var c,d=X+444|0;for(c=d>>2;;){var e=C[c];if(e>>>0<=b>>>0&&(e+y[c+1]|0)>>>0>b>>>0){var g=d;break}c=C[c+2];if((c|0)==0){g=0;break}d=c;c=d>>2}return g}function Gd(b,c){var d=b+8|0,d=(d&7|0)==0?0:-d&7,e=c-d|0;y[X+24>>2]=b+d|0;y[X+12>>2]=e;y[d+(b+4)>>2]=e|1;y[c+(b+4)>>2]=40;y[X+28>>2]=y[Cd+16>>2]}function Hd(b,c,d){var e,g,h,j=c>>2,i=b>>2,f,k=b+8|0,k=(k&7|0)==0?0:-k&7;g=c+8|0;var o=(g&7|0)==0?0:-g&7;h=o>>2;var m=c+o|0,t=k+d|0;g=t>>2;var s=b+t|0,w=m-(b+k)-d|0;y[(k+4>>2)+i]=d|3;d=(m|0)==(y[X+24>>2]|0);a:do{if(d){var u=y[X+12>>2]+w|0;y[X+12>>2]=u;y[X+24>>2]=s;y[g+(i+1)]=u|1}else{if((m|0)==(y[X+20>>2]|0)){u=y[X+8>>2]+w|0;y[X+8>>2]=u;y[X+20>>2]=s;y[g+(i+1)]=u|1;y[(b+u+t|0)>>2]=u}else{var r=C[h+(j+1)];if((r&3|0)==1){var u=r&-8,x=r>>>3,p=r>>>0<256;b:do{if(p){var B=C[((o|8)>>2)+j],I=C[h+(j+3)];if((B|0)==(I|0)){y[X>>2]=y[X>>2]&(1<<x^-1)}else{var S=((r>>>2&1073741822)<<2)+X+40|0;f=(B|0)==(S|0)?15:B>>>0<C[X+16>>2]>>>0?18:15;do{if(f==15&&!((I|0)!=(S|0)&&I>>>0<C[X+16>>2]>>>0)){y[B+12>>2]=I;y[I+8>>2]=B;break b}}while(0);$();a("Reached an unreachable!")}}else{f=m;B=C[((o|24)>>2)+j];I=C[h+(j+3)];S=(I|0)==(f|0);do{if(S){e=o|16;var Q=e+(c+4)|0,H=y[Q>>2];if((H|0)==0){e=c+e|0;H=y[e>>2];if((H|0)==0){H=0;e=H>>2;break}}else{e=Q}for(;;){var Q=H+20|0,J=y[Q>>2];if((J|0)==0){Q=H+16|0;J=C[Q>>2];if((J|0)==0){break}}e=Q;H=J}if(e>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[e>>2]=0}else{e=C[((o|8)>>2)+j];if(e>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[e+12>>2]=I;y[I+8>>2]=e;H=I}e=H>>2}while(0);if((B|0)!=0){I=o+(c+28)|0;S=(y[I>>2]<<2)+X+304|0;Q=(f|0)==(y[S>>2]|0);do{if(Q){y[S>>2]=H;if((H|0)!=0){break}y[X+4>>2]=y[X+4>>2]&(1<<y[I>>2]^-1);break b}if(B>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}J=B+16|0;(y[J>>2]|0)==(f|0)?y[J>>2]=H:y[B+20>>2]=H;if((H|0)==0){break b}}while(0);if(H>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[e+6]=B;f=o|16;B=C[(f>>2)+j];if((B|0)!=0){if(B>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[e+4]=B;y[B+24>>2]=H}f=C[(f+4>>2)+j];if((f|0)!=0){if(f>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}y[e+5]=f;y[f+24>>2]=H}}}}while(0);r=c+(u|o)|0;u=u+w|0}else{r=m;u=w}r=r+4|0;y[r>>2]=y[r>>2]&-2;y[g+(i+1)]=u|1;y[(u>>2)+i+g]=u;if(u>>>0<256){x=u>>>2&1073741822;r=(x<<2)+X+40|0;p=C[X>>2];u=1<<(u>>>3);if((p&u|0)==0){y[X>>2]=p|u;u=r;x=(x+2<<2)+X+40|0}else{x=(x+2<<2)+X+40|0;u=C[x>>2];if(u>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}}y[x>>2]=s;y[u+12>>2]=s;y[g+(i+2)]=u;y[g+(i+3)]=r}else{r=s;p=u>>>8;if((p|0)==0){x=0}else{if(u>>>0>16777215){x=31}else{x=(p+1048320|0)>>>16&8;f=p<<x;p=(f+520192|0)>>>16&4;f=f<<p;B=(f+245760|0)>>>16&2;x=14-(p|x|B)+(f<<B>>>15)|0;x=u>>>((x+7|0)>>>0)&1|x<<1}}p=(x<<2)+X+304|0;y[g+(i+7)]=x;f=t+(b+16)|0;y[g+(i+5)]=0;y[f>>2]=0;f=y[X+4>>2];B=1<<x;if((f&B|0)==0){y[X+4>>2]=f|B;y[p>>2]=r;y[g+(i+6)]=p;y[g+(i+3)]=r;y[g+(i+2)]=r}else{x=u<<((x|0)==31?0:25-(x>>>1)|0);for(p=y[p>>2];;){if((y[p+4>>2]&-8|0)==(u|0)){f=p+8|0;B=C[f>>2];I=C[X+16>>2];S=p>>>0<I>>>0;do{if(!S&&B>>>0>=I>>>0){y[B+12>>2]=r;y[f>>2]=r;y[g+(i+2)]=B;y[g+(i+3)]=p;y[g+(i+6)]=0;break a}}while(0);$();a("Reached an unreachable!")}f=(x>>>31<<2)+p+16|0;B=C[f>>2];if((B|0)==0){if(f>>>0>=C[X+16>>2]>>>0){y[f>>2]=r;y[g+(i+6)]=p;y[g+(i+3)]=r;y[g+(i+2)]=r;break a}$();a("Reached an unreachable!")}x=x<<1;p=B}}}}}}while(0);return b+(k|8)|0}Hd.X=1;function Id(b,c){var d,e,g=C[X+24>>2];e=g>>2;var h=Ed(g),j=y[h>>2];d=y[h+4>>2];var h=j+d|0,i=j+(d-39)|0,j=j+(d-47)+((i&7|0)==0?0:-i&7)|0,j=j>>>0<(g+16|0)>>>0?g:j,i=j+8|0;d=i>>2;Gd(b,c-40|0);y[(j+4|0)>>2]=27;y[d]=y[X+444>>2];y[d+1]=y[X+448>>2];y[d+2]=y[X+452>>2];y[d+3]=y[X+456>>2];y[X+444>>2]=b;y[X+448>>2]=c;y[X+456>>2]=0;y[X+452>>2]=i;d=j+28|0;y[d>>2]=7;i=(j+32|0)>>>0<h>>>0;a:do{if(i){for(var f=d;;){var k=f+4|0;y[k>>2]=7;if((f+8|0)>>>0>=h>>>0){break a}f=k}}}while(0);h=(j|0)==(g|0);a:do{if(!h){d=j-g|0;i=g+d|0;f=d+(g+4)|0;y[f>>2]=y[f>>2]&-2;y[e+1]=d|1;y[i>>2]=d;if(d>>>0<256){f=d>>>2&1073741822;i=(f<<2)+X+40|0;k=C[X>>2];d=1<<(d>>>3);if((k&d|0)==0){y[X>>2]=k|d;d=i;f=(f+2<<2)+X+40|0}else{f=(f+2<<2)+X+40|0;d=C[f>>2];if(d>>>0<C[X+16>>2]>>>0){$();a("Reached an unreachable!")}}y[f>>2]=g;y[d+12>>2]=g;y[e+2]=d;y[e+3]=i}else{i=g;k=d>>>8;if((k|0)==0){f=0}else{if(d>>>0>16777215){f=31}else{var f=(k+1048320|0)>>>16&8,o=k<<f,k=(o+520192|0)>>>16&4,o=o<<k,m=(o+245760|0)>>>16&2,f=14-(k|f|m)+(o<<m>>>15)|0,f=d>>>((f+7|0)>>>0)&1|f<<1}}k=(f<<2)+X+304|0;y[e+7]=f;y[e+5]=0;y[e+4]=0;o=y[X+4>>2];m=1<<f;if((o&m|0)==0){y[X+4>>2]=o|m;y[k>>2]=i;y[e+6]=k;y[e+3]=g;y[e+2]=g}else{f=d<<((f|0)==31?0:25-(f>>>1)|0);for(k=y[k>>2];;){if((y[k+4>>2]&-8|0)==(d|0)){var o=k+8|0,m=C[o>>2],t=C[X+16>>2],s=k>>>0<t>>>0;do{if(!s&&m>>>0>=t>>>0){y[m+12>>2]=i;y[o>>2]=i;y[e+2]=m;y[e+3]=k;y[e+6]=0;break a}}while(0);$();a("Reached an unreachable!")}o=(f>>>31<<2)+k+16|0;m=C[o>>2];if((m|0)==0){if(o>>>0>=C[X+16>>2]>>>0){y[o>>2]=i;y[e+6]=k;y[e+3]=g;y[e+2]=g;break a}$();a("Reached an unreachable!")}f=f<<1;k=m}}}}}while(0)}Id.X=1;function yc(b,c){function d(b){var d;if(b==="double"){d=(Zb[0]=y[c+g>>2],Zb[1]=y[c+g+4>>2],Yb[0])}else{if(b=="i64"){d=[y[c+g>>2],y[c+g+4>>2]]}else{b="i32";d=y[c+g>>2]}}g=g+Math.max(Wa(b),Xa);return d}for(var e=b,g=0,h=[],j,i;;){var f=e;j=v[e];if(j===0){break}i=v[e+1];if(j==37){var k=n,o=n,m=n,t=n;a:for(;;){switch(i){case 43:k=l;break;case 45:o=l;break;case 35:m=l;break;case 48:if(t){break a}else{t=l;break};default:break a}e++;i=v[e+1]}var s=0;if(i==42){s=d("i32");e++;i=v[e+1]}else{for(;i>=48&&i<=57;){s=s*10+(i-48);e++;i=v[e+1]}}var w=n;if(i==46){var u=0,w=l;e++;i=v[e+1];if(i==42){u=d("i32");e++}else{for(;;){i=v[e+1];if(i<48||i>57){break}u=u*10+(i-48);e++}}i=v[e+1]}else{u=6}var r;switch(String.fromCharCode(i)){case"h":i=v[e+2];if(i==104){e++;r=1}else{r=2}break;case"l":i=v[e+2];if(i==108){e++;r=8}else{r=4}break;case"L":case"q":case"j":r=8;break;case"z":case"t":case"I":r=4;break;default:r=pa}r&&e++;i=v[e+1];if(["d","i","u","o","x","X","p"].indexOf(String.fromCharCode(i))!=-1){f=i==100||i==105;r=r||4;j=d("i"+r*8);r==8&&(j=i==117?(j[0]>>>0)+(j[1]>>>0)*4294967296:(j[0]>>>0)+(j[1]|0)*4294967296);r<=4&&(j=(f?vc:uc)(j&Math.pow(256,r)-1,r*8));var x=Math.abs(j),p,f="";if(i==100||i==105){p=vc(j,8*r).toString(10)}else{if(i==117){p=uc(j,8*r).toString(10);j=Math.abs(j)}else{if(i==111){p=(m?"0":"")+x.toString(8)}else{if(i==120||i==88){f=m?"0x":"";if(j<0){j=-j;p=(x-1).toString(16);m=[];for(x=0;x<p.length;x++){m.push((15-parseInt(p[x],16)).toString(16))}for(p=m.join("");p.length<r*2;){p="f"+p}}else{p=x.toString(16)}if(i==88){f=f.toUpperCase();p=p.toUpperCase()}}else{if(i==112){if(x===0){p="(nil)"}else{f="0x";p=x.toString(16)}}}}}}if(w){for(;p.length<u;){p="0"+p}}for(k&&(f=j<0?"-"+f:"+"+f);f.length+p.length<s;){o?p=p+" ":t?p="0"+p:f=" "+f}p=f+p;p.split("").forEach((function(b){h.push(b.charCodeAt(0))}))}else{if(["f","F","e","E","g","G"].indexOf(String.fromCharCode(i))!=-1){j=d("double");if(isNaN(j)){p="nan";t=n}else{if(isFinite(j)){w=n;r=Math.min(u,20);if(i==103||i==71){w=l;u=u||1;r=parseInt(j.toExponential(r).split("e")[1],10);if(u>r&&r>=-4){i=(i==103?"f":"F").charCodeAt(0);u=u-(r+1)}else{i=(i==103?"e":"E").charCodeAt(0);u--}r=Math.min(u,20)}if(i==101||i==69){p=j.toExponential(r);/[eE][-+]\d$/.test(p)&&(p=p.slice(0,-1)+"0"+p.slice(-1))}else{if(i==102||i==70){p=j.toFixed(r)}}f=p.split("e");if(w&&!m){for(;f[0].length>1&&f[0].indexOf(".")!=-1&&(f[0].slice(-1)=="0"||f[0].slice(-1)==".");){f[0]=f[0].slice(0,-1)}}else{for(m&&p.indexOf(".")==-1&&(f[0]=f[0]+".");u>r++;){f[0]=f[0]+"0"}}p=f[0]+(f.length>1?"e"+f[1]:"");i==69&&(p=p.toUpperCase());k&&j>=0&&(p="+"+p)}else{p=(j<0?"-":"")+"inf";t=n}}for(;p.length<s;){p=o?p+" ":t&&(p[0]=="-"||p[0]=="+")?p[0]+"0"+p.slice(1):(t?"0":" ")+p}i<97&&(p=p.toUpperCase());p.split("").forEach((function(b){h.push(b.charCodeAt(0))}))}else{if(i==115){if(k=d("i8*")){k=tc(k);w&&k.length>u&&(k=k.slice(0,u))}else{k=jc("(null)",l)}if(!o){for(;k.length<s--;){h.push(32)}}h=h.concat(k);if(o){for(;k.length<s--;){h.push(32)}}}else{if(i==99){for(o&&h.push(d("i8"));--s>0;){h.push(32)}o||h.push(d("i8"))}else{if(i==110){o=d("i32*");y[o>>2]=h.length}else{if(i==37){h.push(j)}else{for(x=f;x<e+2;x++){h.push(v[x])}}}}}}}e=e+2}else{h.push(j);e=e+1}}return h}var Pd=13,Qd=9,Rd=22,Sd=5,Vd=21,Wd=6;function Xd(b){Jd||(Jd=G([0],"i32",D));y[Jd>>2]=b}var Jd,Yd=0,zc=0,Zd=0,$d=2,Ec=[pa],ae=l;function be(b,c){if(typeof b!=="string"){return pa}c===aa&&(c="/");b&&b[0]=="/"&&(c="");for(var d=(c+"/"+b).split("/").reverse(),e=[""];d.length;){var g=d.pop();g==""||g=="."||(g==".."?e.length>1&&e.pop():e.push(g))}return e.length==1?"/":e.join("/")}function ce(b,c,d){var e={ga:n,l:n,error:0,name:pa,path:pa,object:pa,v:n,z:pa,w:pa},b=be(b);if(b=="/"){e.ga=l;e.l=e.v=l;e.name="/";e.path=e.z="/";e.object=e.w=de}else{if(b!==pa){for(var d=d||0,b=b.slice(1).split("/"),g=de,h=[""];b.length;){if(b.length==1&&g.d){e.v=l;e.z=h.length==1?"/":h.join("/");e.w=g;e.name=b[0]}var j=b.shift();if(g.d){if(g.A){if(!g.c.hasOwnProperty(j)){e.error=2;break}}else{e.error=Pd;break}}else{e.error=20;break}g=g.c[j];if(g.link&&!(c&&b.length==0)){if(d>40){e.error=40;break}e=be(g.link,h.join("/"));return ce([e].concat(b).join("/"),c,d+1)}h.push(j);if(b.length==0){e.l=l;e.path=h.join("/");e.object=g}}}}return e}function ee(b){fe();b=ce(b,aa);if(b.l){return b.object}Xd(b.error);return pa}function ge(b,c,d,e,g){b||(b="/");typeof b==="string"&&(b=ee(b));if(!b){Xd(Pd);a(Error("Parent path must exist."))}if(!b.d){Xd(20);a(Error("Parent must be a folder."))}if(!b.write&&!ae){Xd(Pd);a(Error("Parent folder must be writeable."))}if(!c||c=="."||c==".."){Xd(2);a(Error("Name must not be empty."))}if(b.c.hasOwnProperty(c)){Xd(17);a(Error("Can't overwrite object."))}b.c[c]={A:e===aa?l:e,write:g===aa?n:g,timestamp:Date.now(),fa:$d++};for(var h in d){d.hasOwnProperty(h)&&(b.c[c][h]=d[h])}return b.c[c]}function he(b,c){return ge(b,c,{d:l,h:n,c:{}},l,l)}function ie(){var b="dev/shm/tmp",c=ee("/");c===pa&&a(Error("Invalid parent."));for(b=b.split("/").reverse();b.length;){var d=b.pop();if(d){c.c.hasOwnProperty(d)||he(c,d);c=c.c[d]}}}function je(b,c,d,e){!d&&!e&&a(Error("A device must have at least one callback defined."));var g={h:l,input:d,e:e};g.d=n;return ge(b,c,g,Boolean(d),Boolean(e))}function fe(){de||(de={A:l,write:l,d:l,h:n,timestamp:Date.now(),fa:1,c:{}})}function ke(){var b,c,d;function e(b){if(b===pa||b===10){c.j(c.buffer.join(""));c.buffer=[]}else{c.buffer.push(String.fromCharCode(b))}}Ya(!le,"FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)");le=l;fe();b=b||Module.stdin;c=c||Module.stdout;d=d||Module.stderr;var g=l,h=l,j=l;if(!b){g=n;b=(function(){if(!b.k||!b.k.length){var c;typeof window!="undefined"&&typeof window.prompt=="function"?c=window.prompt("Input: "):typeof readline=="function"&&(c=readline());c||(c="");b.k=jc(c+"\n",l)}return b.k.shift()})}if(!c){h=n;c=e}if(!c.j){c.j=print}if(!c.buffer){c.buffer=[]}if(!d){j=n;d=e}if(!d.j){d.j=print}if(!d.buffer){d.buffer=[]}he("/","tmp");var i=he("/","dev"),f=je(i,"stdin",b),k=je(i,"stdout",pa,c);d=je(i,"stderr",pa,d);je(i,"tty",b,c);Ec[1]={path:"/dev/stdin",object:f,position:0,t:l,i:n,s:n,u:!g,error:n,q:n,B:[]};Ec[2]={path:"/dev/stdout",object:k,position:0,t:n,i:l,s:n,u:!h,error:n,q:n,B:[]};Ec[3]={path:"/dev/stderr",object:d,position:0,t:n,i:l,s:n,u:!j,error:n,q:n,B:[]};Yd=G([1],"void*",D);zc=G([2],"void*",D);Zd=G([3],"void*",D);ie();Ec[Yd]=Ec[1];Ec[zc]=Ec[2];Ec[Zd]=Ec[3];G([G([0,0,0,0,Yd,0,0,0,zc,0,0,0,Zd,0,0,0],"void*",D)],"void*",D)}var le,de;function Dc(b,c,d){var e=Ec[b];if(e){if(e.i){if(d<0){Xd(Rd);return-1}if(e.object.h){if(e.object.e){for(var g=0;g<d;g++){try{e.object.e(v[c+g])}catch(h){Xd(Sd);return-1}}e.object.timestamp=Date.now();return g}Xd(Wd);return-1}g=e.position;b=Ec[b];if(!b||b.object.h){Xd(Qd);c=-1}else{if(b.i){if(b.object.d){Xd(Vd);c=-1}else{if(d<0||g<0){Xd(Rd);c=-1}else{for(var j=b.object.c;j.length<g;){j.push(0)}for(var i=0;i<d;i++){j[g+i]=z[c+i]}b.object.timestamp=Date.now();c=i}}}else{Xd(Pd);c=-1}}if(c!=-1){e.position=e.position+c}return c}Xd(Pd);return-1}Xd(Qd);return-1}function bc(b,c,d){if(d>=20){for(d=b+d;b%4;){v[b++]=c}c<0&&(c=c+256);for(var b=b>>2,e=d>>2,g=c|c<<8|c<<16|c<<24;b<e;){y[b++]=g}for(b=b<<2;b<d;){v[b++]=c}}else{for(;d--;){v[b++]=c}}}function $(){a("ABORT: undefined, at "+Error().stack)}function Nd(){switch(8){case 8:return ec;case 54:case 56:case 21:case 61:case 63:case 22:case 67:case 23:case 24:case 25:case 26:case 27:case 69:case 28:case 101:case 70:case 71:case 29:case 30:case 199:case 75:case 76:case 32:case 43:case 44:case 80:case 46:case 47:case 45:case 48:case 49:case 42:case 82:case 33:case 7:case 108:case 109:case 107:case 112:case 119:case 121:return 200809;case 13:case 104:case 94:case 95:case 34:case 35:case 77:case 81:case 83:case 84:case 85:case 86:case 87:case 88:case 89:case 90:case 91:case 94:case 95:case 110:case 111:case 113:case 114:case 115:case 116:case 117:case 118:case 120:case 40:case 16:case 79:case 19:return-1;case 92:case 93:case 5:case 72:case 6:case 74:case 92:case 93:case 96:case 97:case 98:case 99:case 102:case 103:case 105:return 1;case 38:case 66:case 50:case 51:case 4:return 1024;case 15:case 64:case 41:return 32;case 55:case 37:case 17:return 2147483647;case 18:case 1:return 47839;case 59:case 57:return 99;case 68:case 58:return 2048;case 0:return 2097152;case 3:return 65536;case 14:return 32768;case 73:return 32767;case 39:return 16384;case 60:return 1e3;case 106:return 700;case 52:return 256;case 62:return 255;case 2:return 100;case 65:return 64;case 36:return 20;case 100:return 16;case 20:return 6;case 53:return 4}Xd(Rd);return-1}function Fd(b){if(!me){eb=eb+4095>>12<<12;me=l}var c=eb;b!=0&&db(b);return c}var me;pc.unshift({r:(function(){ae=n;le||ke()})});qc.push({r:(function(){if(le){Ec[2]&&Ec[2].object.e.buffer.length>0&&Ec[2].object.e(10);Ec[3]&&Ec[3].object.e.buffer.length>0&&Ec[3].object.e(10)}})});Xd(0);G(12,"void*",D);Module.ea=(function(b){function c(){for(var b=0;b<3;b++){e.push(0)}}var d=b.length+1,e=[G(jc("/bin/this.program"),"i8",D)];c();for(var g=0;g<d-1;g=g+1){e.push(G(jc(b[g]),"i8",D));c()}e.push(0);e=G(e,"i32",D);return _main(d,e,0)});var Gc,Hc,vd,X,Cd,ne,oe,pe,qe,re;M.G=G([37,115,40,37,117,41,58,32,65,115,115,101,114,116,105,111,110,32,102,97,105,108,117,114,101,58,32,34,37,115,34,10,0],"i8",D);M.H=G([109,95,115,105,122,101,32,60,61,32,109,95,99,97,112,97,99,105,116,121,0],"i8",D);M.O=G([46,47,99,114,110,95,100,101,99,111,109,112,46,104,0],"i8",D);M.T=G([109,105,110,95,110,101,119,95,99,97,112,97,99,105,116,121,32,60,32,40,48,120,55,70,70,70,48,48,48,48,85,32,47,32,101,108,101,109,101,110,116,95,115,105,122,101,41,0],"i8",D);M.Y=G([110,101,119,95,99,97,112,97,99,105,116,121,32,38,38,32,40,110,101,119,95,99,97,112,97,99,105,116,121,32,62,32,109,95,99,97,112,97,99,105,116,121,41,0],"i8",D);M.Z=G([110,117,109,95,99,111,100,101,115,91,99,93,0],"i8",D);M.$=G([115,111,114,116,101,100,95,112,111,115,32,60,32,116,111,116,97,108,95,117,115,101,100,95,115,121,109,115,0],"i8",D);M.aa=G([112,67,111,100,101,115,105,122,101,115,91,115,121,109,95,105,110,100,101,120,93,32,61,61,32,99,111,100,101,115,105,122,101,0],"i8",D);M.ba=G([116,32,60,32,40,49,85,32,60,60,32,116,97,98,108,101,95,98,105,116,115,41,0],"i8",D);M.ca=G([109,95,108,111,111,107,117,112,91,116,93,32,61,61,32,99,85,73,78,84,51,50,95,77,65,88,0],"i8",D);Gc=G([2],["i8* (i8*, i32, i32*, i1, i8*)*",0,0,0,0],D);G([4],["i32 (i8*, i8*)*",0,0,0,0],D);Hc=G(1,"i8*",D);M.m=G([99,114,110,100,95,109,97,108,108,111,99,58,32,115,105,122,101,32,116,111,111,32,98,105,103,0],"i8",D);M.I=G([99,114,110,100,95,109,97,108,108,111,99,58,32,111,117,116,32,111,102,32,109,101,109,111,114,121,0],"i8",D);M.n=G([40,40,117,105,110,116,51,50,41,112,95,110,101,119,32,38,32,40,67,82,78,68,95,77,73,78,95,65,76,76,79,67,95,65,76,73,71,78,77,69,78,84,32,45,32,49,41,41,32,61,61,32,48,0],"i8",D);M.J=G([99,114,110,100,95,114,101,97,108,108,111,99,58,32,98,97,100,32,112,116,114,0],"i8",D);M.K=G([99,114,110,100,95,102,114,101,101,58,32,98,97,100,32,112,116,114,0],"i8",D);M.ma=G([99,114,110,100,95,109,115,105,122,101,58,32,98,97,100,32,112,116,114,0],"i8",D);G([1,0,0,0,2,0,0,0,4,0,0,0,8,0,0,0,16,0,0,0,32,0,0,0,64,0,0,0,128,0,0,0,256,0,0,0,512,0,0,0,1024,0,0,0,2048,0,0,0,4096,0,0,0,8192,0,0,0,16384,0,0,0,32768,0,0,0,65536,0,0,0,131072,0,0,0,262144,0,0,0,524288,0,0,0,1048576,0,0,0,2097152,0,0,0,4194304,0,0,0,8388608,0,0,0,16777216,0,0,0,33554432,0,0,0,67108864,0,0,0,134217728,0,0,0,268435456,0,0,0,536870912,0,0,0,1073741824,0,0,0,-2147483648,0,0,0],["i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0],D);M.M=G([102,97,108,115,101,0],"i8",D);M.na=G([99,114,110,100,95,118,97,108,105,100,97,116,101,95,102,105,108,101,40,38,110,101,119,95,104,101,97,100,101,114,44,32,97,99,116,117,97,108,95,98,97,115,101,95,100,97,116,97,95,115,105,122,101,44,32,78,85,76,76,41,0],"i8",D);M.oa=G([40,116,111,116,97,108,95,115,121,109,115,32,62,61,32,49,41,32,38,38,32,40,116,111,116,97,108,95,115,121,109,115,32,60,61,32,112,114,101,102,105,120,95,99,111,100,105,110,103,58,58,99,77,97,120,83,117,112,112,111,114,116,101,100,83,121,109,115,41,32,38,38,32,40,99,111,100,101,95,115,105,122,101,95,108,105,109,105,116,32,62,61,32,49,41,0],"i8",D);M.N=G([40,116,111,116,97,108,95,115,121,109,115,32,62,61,32,49,41,32,38,38,32,40,116,111,116,97,108,95,115,121,109,115,32,60,61,32,112,114,101,102,105,120,95,99,111,100,105,110,103,58,58,99,77,97,120,83,117,112,112,111,114,116,101,100,83,121,109,115,41,0],"i8",D);M.C=G([17,18,19,20,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15,16],"i8",D);M.o=G([48,0],"i8",D);M.P=G([110,117,109,95,98,105,116,115,32,60,61,32,51,50,85,0],"i8",D);M.Q=G([109,95,98,105,116,95,99,111,117,110,116,32,60,61,32,99,66,105,116,66,117,102,83,105,122,101,0],"i8",D);M.R=G([116,32,33,61,32,99,85,73,78,84,51,50,95,77,65,88,0],"i8",D);M.S=G([109,111,100,101,108,46,109,95,99,111,100,101,95,115,105,122,101,115,91,115,121,109,93,32,61,61,32,108,101,110,0],"i8",D);G([1,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,7,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,6,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,7,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,8,0,0,0],["i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0],D);G([0,0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,4,0,0,0,1,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,3,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,8,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,5,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,6,0,0,0,0,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,7,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,4,0,0,0,8,0,0,0],["i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0],D);M.ha=G([0,3,1,2],"i8",D);M.b=G([0,2,3,1],"i8",D);M.ia=G([0,7,1,2,3,4,5,6],"i8",D);M.a=G([0,2,3,4,5,6,7,1],"i8",D);M.ja=G([1,0,5,4,3,2,6,7],"i8",D);M.ka=G([1,0,7,6,5,4,3,2],"i8",D);M.qa=G([105,110,100,101,120,32,60,32,50,0],"i8",D);M.ra=G([40,108,111,32,60,61,32,48,120,70,70,70,70,85,41,32,38,38,32,40,104,105,32,60,61,32,48,120,70,70,70,70,85,41,0],"i8",D);M.sa=G([40,120,32,60,32,99,68,88,84,66,108,111,99,107,83,105,122,101,41,32,38,38,32,40,121,32,60,32,99,68,88,84,66,108,111,99,107,83,105,122,101,41,0],"i8",D);M.ta=G([118,97,108,117,101,32,60,61,32,48,120,70,70,0],"i8",D);M.ua=G([118,97,108,117,101,32,60,61,32,48,120,70,0],"i8",D);M.va=G([40,108,111,32,60,61,32,48,120,70,70,41,32,38,38,32,40,104,105,32,60,61,32,48,120,70,70,41,0],"i8",D);M.g=G([105,32,60,32,109,95,115,105,122,101,0],"i8",D);M.p=G([110,117,109,32,38,38,32,40,110,117,109,32,61,61,32,126,110,117,109,95,99,104,101,99,107,41,0],"i8",D);M.f=G([1,2,2,3,3,3,3,4],"i8",D);vd=G([0,0,0,0,0,0,1,1,0,1,0,1,0,0,1,2,1,2,0,0,0,1,0,2,1,0,2,0,0,1,2,3],"i8",D);M.U=G([110,101,120,116,95,108,101,118,101,108,95,111,102,115,32,62,32,99,117,114,95,108,101,118,101,108,95,111,102,115,0],"i8",D);M.W=G([40,108,101,110,32,62,61,32,49,41,32,38,38,32,40,108,101,110,32,60,61,32,99,77,97,120,69,120,112,101,99,116,101,100,67,111,100,101,83,105,122,101,41,0],"i8",D);X=G(468,["i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"i32",0,0,0,"i32",0,0,0,"i32",0,0,0,"*",0,0,0,"i32",0,0,0,"*",0,0,0,"i32",0,0,0,"*",0,0,0,"i32",0,0,0],D);Cd=G(24,"i32",D);M.wa=G([109,97,120,32,115,121,115,116,101,109,32,98,121,116,101,115,32,61,32,37,49,48,108,117,10,0],"i8",D);M.la=G([115,121,115,116,101,109,32,98,121,116,101,115,32,32,32,32,32,61,32,37,49,48,108,117,10,0],"i8",D);M.pa=G([105,110,32,117,115,101,32,98,121,116,101,115,32,32,32,32,32,61,32,37,49,48,108,117,10,0],"i8",D);G([0],"i8",D);G(1,"void ()*",D);ne=G([0,0,0,0,0,0,0,0,6,0,0,0,8,0,0,0,10,0,0,0],["*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0],D);G(1,"void*",D);M.V=G([115,116,100,58,58,98,97,100,95,97,108,108,111,99,0],"i8",D);oe=G([0,0,0,0,0,0,0,0,12,0,0,0,14,0,0,0,16,0,0,0],["*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0,"*",0,0,0],D);G(1,"void*",D);M.L=G([98,97,100,95,97,114,114,97,121,95,110,101,119,95,108,101,110,103,116,104,0],"i8",D);M.F=G([83,116,57,98,97,100,95,97,108,108,111,99,0],"i8",D);qe=G(12,"*",D);M.D=G([83,116,50,48,98,97,100,95,97,114,114,97,121,95,110,101,119,95,108,101,110,103,116,104,0],"i8",D);re=G(12,"*",D);y[ne+4>>2]=qe;y[oe+4>>2]=re;pe=G([2,0,0,0,0],["i8*",0,0,0,0],D);y[qe>>2]=pe+8|0;y[qe+4>>2]=M.F|0;y[qe+8>>2]=aa;y[re>>2]=pe+8|0;y[re+4>>2]=M.D|0;y[re+8>>2]=qe;dc=[0,0,(function(b,c,d,e){if((b|0)==0){b=ac(c);(d|0)!=0&&(y[d>>2]=(b|0)==0?0:Od(b));d=b}else{if((c|0)==0){Ld(b);(d|0)!=0&&(y[d>>2]=0);d=0}else{var g=(b|0)==0?ac(c):Md(b,c),h=(g|0)!=0,j=h?g:b;if(h|e^1){b=j}else{g=(b|0)==0?ac(c):Md(b,c);(g|0)==0?g=0:b=g}(d|0)!=0&&(y[d>>2]=Od(b));d=g}}return d}),0,(function(b){return(b|0)==0?0:Od(b)}),0,(function(b){aa(b|0)}),0,(function(b){aa(b|0);(b|0)!=0&&Ld(b)}),0,(function(){return M.V|0}),0,(function(b){aa(b|0)}),0,(function(b){aa(b|0);(b|0)!=0&&Ld(b)}),0,(function(){return M.L|0}),0];Module.FUNCTION_TABLE=dc;function se(b){b=b||Module.arguments;nc(pc);var c=pa;if(Module._main){c=Module.ea(b);Module.noExitRuntime||nc(qc)}return c}Module.run=se;Module.preRun&&Module.preRun();Module.noInitialRun||se();Module.postRun&&Module.postRun() + + //=== + +onmessage = function(msg) { + var start = Date.now(); + var data = deCrunch(new Uint8Array(msg.data.data), msg.data.filename); + postMessage({ + filename: msg.data.filename, + data: data, + callbackID: msg.data.callbackID, + time: Date.now() - start + }); +}; + diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee index f83b4a52..05e3788b 100644 --- a/tools/eliminator/eliminator.coffee +++ b/tools/eliminator/eliminator.coffee @@ -383,14 +383,8 @@ class ExpressionOptimizer # function, then writes the optimized result to stdout. main = -> # Get the parse tree. - if os.platform().substr(0, 3) != 'win' - src = fs.readFileSync('/dev/stdin').toString() - else - # The following seems to work on windows, but fails on linux.. - src = '' - size = fs.fstatSync(process.stdin.fd).size - if size > 0 - src = fs.readSync(process.stdin.fd, size)[0] + #process.stderr.write(JSON.stringify(process.argv[2]) + '\n') + src = fs.readFileSync(process.argv[2]).toString() throw 'Cannot identify generated functions' if GENERATED_FUNCTIONS_MARKER in src generatedFunctionsLine = src.split('\n').filter (line) -> @@ -399,22 +393,22 @@ main = -> ast = uglify.parser.parse src - #process.stderr.write(JSON.stringify(ast) + '\n') + ##process.stderr.write(JSON.stringify(ast) + '\n') # Run on all functions. traverse ast, (node, type) -> if type in ['defun', 'function'] and isGenerated node[1] # Run the eliminator - process.stderr.write (node[1] || '(anonymous)') + '\n' + #process.stderr.write (node[1] || '(anonymous)') + '\n' eliminated = new Eliminator(node).run() if eliminated? - process.stderr.write " Eliminated #{eliminated} vars.\n" + #process.stderr.write " Eliminated #{eliminated} vars.\n" # Run the expression optimizer new ExpressionOptimizer(node[3]).run() else - process.stderr.write ' Skipped.\n' + #process.stderr.write ' Skipped.\n' return undefined diff --git a/tools/file_packager.py b/tools/file_packager.py new file mode 100644 index 00000000..3844b4ae --- /dev/null +++ b/tools/file_packager.py @@ -0,0 +1,399 @@ +''' +A tool that generates FS API calls to generate a filesystem, and packages the files +to work with that. + +This is called by emcc. You can also call it yourself. + +Usage: + + file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run] [--crunch[=X]] + +Notes: + + --pre-run Will generate wrapper code that does preloading in Module.preRun. This is necessary if you add this + code before the main file has been loading, which includes necessary components like addRunDependency. + + --crunch=X Will compress dxt files to crn with quality level X. The crunch commandline tool must be present + and CRUNCH should be defined in ~/.emscripten that points to it. JS crunch decompressing code will + be added to convert the crn to dds in the browser. + crunch-worker.js will be generated in the current directory. You should include that file when + packaging your site. + DDS files will not be crunched if the .crn is more recent than the .dds. This prevents a lot of + unneeded computation. + +TODO: You can also provide .crn files yourself, pre-crunched. With this option, they will be decompressed + to dds files in the browser, exactly the same as if this tool compressed them. +''' + +import os, sys, shutil + +from shared import Compression, execute, suffix, unsuffixed +import shared +from subprocess import Popen, PIPE, STDOUT + +data_target = sys.argv[1] + +IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') +AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3') +AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' } +CRUNCH_INPUT_SUFFIX = '.dds' +CRUNCH_OUTPUT_SUFFIX = '.crn' + +DDS_HEADER_SIZE = 128 + +data_files = [] +in_preload = False +in_embed = False +has_preloaded = False +in_compress = 0 +pre_run = False +crunch = 0 + +for arg in sys.argv[1:]: + if arg == '--preload': + in_preload = True + in_embed = False + has_preloaded = True + in_compress = 0 + elif arg == '--embed': + in_embed = True + in_preload = False + in_compress = 0 + elif arg == '--compress': + Compression.on = True + in_compress = 1 + in_preload = False + in_embed = False + elif arg == '--pre-run': + pre_run = True + in_preload = False + in_embed = False + in_compress = 0 + elif arg.startswith('--crunch'): + from shared import CRUNCH + crunch = arg.split('=')[1] if '=' in arg else '128' + in_preload = False + in_embed = False + in_compress = 0 + elif in_preload: + data_files.append({ 'name': arg, 'mode': 'preload' }) + elif in_embed: + data_files.append({ 'name': arg, 'mode': 'embed' }) + elif in_compress: + if in_compress == 1: + Compression.encoder = arg + in_compress = 2 + elif in_compress == 2: + Compression.decoder = arg + in_compress = 3 + elif in_compress == 3: + Compression.js_name = arg + in_compress = 0 + +code = ''' +function assert(check, msg) { + if (!check) throw msg; +} +''' + +if has_preloaded: + code += ''' + var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs")); + var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs"); + var hasBlobConstructor; + try { + new Blob(); + hasBlobConstructor = true; + } catch(e) { + hasBlobConstructor = false; + console.log("warning: no blob constructor, cannot create blobs with mimetypes"); + } +''' + + code += 'Module["preloadedImages"] = {}; // maps url to image data\n' + code += 'Module["preloadedAudios"] = {}; // maps url to audio data\n' + +# Expand directories into individual files +def add(mode, dirname, names): + for name in names: + fullname = os.path.join(dirname, name) + if not os.path.isdir(fullname): + data_files.append({ 'name': fullname, 'mode': mode }) + +for file_ in data_files: + if os.path.isdir(file_['name']): + os.path.walk(file_['name'], add, file_['mode']) +data_files = filter(lambda file_: not os.path.isdir(file_['name']), data_files) + +for file_ in data_files: + file_['name'] = file_['name'].replace(os.path.sep, '/') # name in the filesystem, native and emulated + file_['localname'] = file_['name'] # name to actually load from local filesystem, after transformations + +# Remove duplicates (can occur naively, for example preload dir/, preload dir/subdir/) +seen = {} +def was_seen(name): + if seen.get(name): return True + seen[name] = 1 + return False +data_files = filter(lambda file_: not was_seen(file_['name']), data_files) + +# Crunch files +if crunch: + shutil.copyfile(shared.path_from_root('tools', 'crunch-worker.js'), 'crunch-worker.js') + print ''' + var decrunchWorker = new Worker('crunch-worker.js'); + var decrunchCallbacks = []; + decrunchWorker.onmessage = function(msg) { + decrunchCallbacks[msg.data.callbackID](msg.data.data); + console.log('decrunched ' + msg.data.filename + ' in ' + msg.data.time + ' ms, ' + msg.data.data.length + ' bytes'); + decrunchCallbacks[msg.data.callbackID] = null; + }; + function requestDecrunch(filename, data, callback) { + decrunchWorker.postMessage({ + filename: filename, + data: data, + callbackID: decrunchCallbacks.length + }); + decrunchCallbacks.push(callback); + } +''' + + for file_ in data_files: + if file_['name'].endswith(CRUNCH_INPUT_SUFFIX): + # Do not crunch if crunched version exists and is more recent than dds source + crunch_name = unsuffixed(file_['name']) + CRUNCH_OUTPUT_SUFFIX + file_['localname'] = crunch_name + try: + crunch_time = os.stat(crunch_name).st_mtime + dds_time = os.stat(file_['name']).st_mtime + if dds_time < crunch_time: continue + except: + pass # if one of them does not exist, continue on + + # guess at format. this lets us tell crunch to not try to be clever and use odd formats like DXT5_AGBR + try: + format = Popen(['file', file_['name']], stdout=PIPE).communicate()[0] + if 'DXT5' in format: + format = ['-dxt5'] + elif 'DXT1' in format: + format = ['-dxt1'] + else: + raise Exception('unknown format') + except: + format = [] + Popen([CRUNCH, '-file', file_['name'], '-quality', crunch] + format, stdout=sys.stderr).communicate() + shutil.move(os.path.basename(crunch_name), crunch_name) # crunch places files in the current dir + # prepend the dds header + crunched = open(crunch_name, 'rb').read() + c = open(crunch_name, 'wb') + c.write(open(file_['name'], 'rb').read()[:DDS_HEADER_SIZE]) + c.write(crunched) + c.close() + +# Set up folders +partial_dirs = [] +for file_ in data_files: + dirname = os.path.dirname(file_['name']) + dirname = dirname.lstrip('/') # absolute paths start with '/', remove that + if dirname != '': + parts = dirname.split('/') + for i in range(len(parts)): + partial = '/'.join(parts[:i+1]) + if partial not in partial_dirs: + code += '''Module['FS_createFolder']('/%s', '%s', true, true);\n''' % ('/'.join(parts[:i]), parts[i]) + partial_dirs.append(partial) + +if has_preloaded: + # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead. + data = open(data_target, 'wb') + start = 0 + for file_ in data_files: + file_['data_start'] = start + curr = open(file_['localname'], 'rb').read() + file_['data_end'] = start + len(curr) + print >> sys.stderr, 'bundling', file_['name'], file_['localname'], file_['data_start'], file_['data_end'] + start += len(curr) + data.write(curr) + data.close() + if Compression.on: + Compression.compress(data_target) + + # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs + code += ''' + function DataRequest() {} + DataRequest.prototype = { + requests: {}, + open: function(mode, name) { + this.requests[name] = this; + }, + send: function() {} + }; + ''' + +counter = 0 +for file_ in data_files: + filename = file_['name'] + if file_['mode'] == 'embed': + # Embed + code += '''Module['FS_createDataFile']('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(file_['localname'], 'rb').read()))) + elif file_['mode'] == 'preload': + # Preload + varname = 'filePreload%d' % counter + counter += 1 + image = filename.endswith(IMAGE_SUFFIXES) + audio = filename.endswith(AUDIO_SUFFIXES) + dds = crunch and filename.endswith(CRUNCH_INPUT_SUFFIX) + + prepare = '' + finish = "Module['removeRunDependency']();\n" + + if image: + finish = ''' + var bb = new BlobBuilder(); + bb.append(byteArray.buffer); + var b = bb.getBlob(); + var url = URLObject.createObjectURL(b); + var img = new Image(); + img.onload = function() { + assert(img.complete, 'Image %(filename)s could not be decoded'); + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + Module["preloadedImages"]['%(filename)s'] = canvas; + URLObject.revokeObjectURL(url); + Module['removeRunDependency'](); + }; + img.onerror = function(event) { + console.log('Image %(filename)s could not be decoded'); + }; + img.src = url; +''' % { 'filename': filename } + elif audio: + # Need actual blob constructor here, to set the mimetype or else audios fail to decode + finish = ''' + if (hasBlobConstructor) { + var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' }); + var url = URLObject.createObjectURL(b); // XXX we never revoke this! + var audio = new Audio(); + audio.removedDependency = false; + audio['oncanplaythrough'] = function() { // XXX string for closure + audio['oncanplaythrough'] = null; + Module["preloadedAudios"]['%(filename)s'] = audio; + if (!audio.removedDependency) { + Module['removeRunDependency'](); + audio.removedDependency = true; + } + }; + audio.onerror = function(event) { + if (!audio.removedDependency) { + console.log('Audio %(filename)s could not be decoded or timed out trying to decode'); + Module['removeRunDependency'](); + audio.removedDependency = true; + } + }; + setTimeout(audio.onerror, 2000); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang) + audio.src = url; + } else { + Module["preloadedAudios"]['%(filename)s'] = new Audio(); // empty shim + Module['removeRunDependency'](); + } +''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] } + elif dds: + # decompress crunch format into dds + prepare = ''' + var ddsHeader = byteArray.subarray(0, %(dds_header_size)d); + requestDecrunch('%(filename)s', byteArray.subarray(%(dds_header_size)d), function(ddsData) { + byteArray = new Uint8Array(ddsHeader.length + ddsData.length); + byteArray.set(ddsHeader, 0); + byteArray.set(ddsData, %(dds_header_size)d); +''' % { 'filename': filename, 'dds_header_size': DDS_HEADER_SIZE } + + finish = ''' + Module['removeRunDependency'](); + }); +''' + + code += ''' + var %(varname)s = new %(request)s(); + %(varname)s.open('GET', '%(filename)s', true); + %(varname)s.responseType = 'arraybuffer'; + %(varname)s.onload = function() { + var arrayBuffer = %(varname)s.response; + assert(arrayBuffer, 'Loading file %(filename)s failed.'); + var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer; + %(prepare)s + Module['FS_createDataFile']('/%(dirname)s', '%(basename)s', byteArray, true, true); + %(finish)s + }; + Module['addRunDependency'](); + %(varname)s.send(null); +''' % { + 'request': 'DataRequest', # In the past we also supported XHRs here + 'varname': varname, + 'filename': filename, + 'dirname': os.path.dirname(filename), + 'basename': os.path.basename(filename), + 'prepare': prepare, + 'finish': finish + } + else: + assert 0 + +if has_preloaded: + # Get the big archive and split it up + use_data = '' + for file_ in data_files: + if file_['mode'] == 'preload': + use_data += ''' + curr = DataRequest.prototype.requests['%s']; + curr.response = byteArray.subarray(%d,%d); + curr.onload(); + ''' % (file_['name'], file_['data_start'], file_['data_end']) + use_data += " Module['removeRunDependency']();\n" + + if Compression.on: + use_data = ''' + Module["decompress"](byteArray, function(decompressed) { + byteArray = new Uint8Array(decompressed); + %s + }); +''' % use_data + + code += ''' + var dataFile = new XMLHttpRequest(); + dataFile.open('GET', '%s', true); + dataFile.responseType = 'arraybuffer'; + dataFile.onload = function() { + var arrayBuffer = dataFile.response; + assert(arrayBuffer, 'Loading data file failed.'); + var byteArray = new Uint8Array(arrayBuffer); + var curr; + %s + }; + Module['addRunDependency'](); + dataFile.send(null); + if (Module['setStatus']) Module['setStatus']('Downloading...'); + ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data) + +if pre_run: + print ''' + if (typeof Module == 'undefined') Module = {}; + if (!Module['preRun']) Module['preRun'] = []; + Module["preRun"].push(function() { +''' + +print code + +if pre_run: + print ' });\n' + +if crunch: + print ''' + if (!Module['postRun']) Module['postRun'] = []; + Module["postRun"].push(function() { + decrunchWorker.terminate(); + }); +''' + diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index 2f6b2ff3..29887e62 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -1312,7 +1312,7 @@ function registerize(ast) { } getStatements(fun).unshift(['var', vars]); } - printErr(fun[1] + ': saved ' + saved + ' / ' + (saved + nextReg - 1) + ' vars through registerization'); // not totally accurate + //printErr(fun[1] + ': saved ' + saved + ' / ' + (saved + nextReg - 1) + ' vars through registerization'); // not totally accurate }); } diff --git a/tools/shared.py b/tools/shared.py index f71bec72..0d99a22d 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -114,6 +114,7 @@ LLVM_DIS=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-dis')) LLVM_NM=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-nm')) LLVM_INTERPRETER=os.path.expanduser(os.path.join(LLVM_ROOT, 'lli')) LLVM_COMPILER=os.path.expanduser(os.path.join(LLVM_ROOT, 'llc')) +LLVM_EXTRACT=os.path.expanduser(os.path.join(LLVM_ROOT, 'llvm-extract')) COFFEESCRIPT = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee') EMSCRIPTEN = path_from_root('emscripten.py') @@ -131,6 +132,7 @@ BINDINGS_GENERATOR = path_from_root('tools', 'bindings_generator.py') EXEC_LLVM = path_from_root('tools', 'exec_llvm.py') VARIABLE_ELIMINATOR = path_from_root('tools', 'eliminator', 'eliminator.coffee') JS_OPTIMIZER = path_from_root('tools', 'js-optimizer.js') +FILE_PACKAGER = path_from_root('tools', 'file_packager.py') # Temp dir. Create a random one, unless EMCC_DEBUG is set, in which case use TEMP_DIR/emscripten_temp @@ -196,8 +198,10 @@ USE_EMSDK = not os.environ.get('EMMAKEN_NO_SDK') if USE_EMSDK: # Disable system C and C++ include directories, and add our own (using -idirafter so they are last, like system dirs, which # allows projects to override them) - EMSDK_OPTS = ['-nostdinc', '-nostdinc++', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdinc++', '-Xclang', '-nostdsysteminc', + # Note that -nostdinc++ is not needed, since -nostdinc implies that! + EMSDK_OPTS = ['-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc', '-Xclang', '-isystem' + path_from_root('system', 'include'), + '-Xclang', '-isystem' + path_from_root('system', 'include', 'emscripten'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'bsd'), # posix stuff '-Xclang', '-isystem' + path_from_root('system', 'include', 'libc'), '-Xclang', '-isystem' + path_from_root('system', 'include', 'libcxx'), @@ -528,7 +532,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e return generated_libs @staticmethod - def link(files, target): + def link(files, target, remove_duplicates=False): actual_files = [] unresolved_symbols = set() # necessary for .a linking, see below resolved_symbols = set() @@ -577,8 +581,27 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e finally: os.chdir(cwd) try_delete(target) + + if remove_duplicates: + # Remove duplicate symbols. This is a workaround for how we compile .a files, we try to + # emulate ld behavior which is permissive TODO: cache llvm-nm results + seen_symbols = set() + print >> sys.stderr, actual_files + for actual in actual_files: + symbols = Building.llvm_nm(actual) + dupes = seen_symbols.intersection(symbols.defs) + if len(dupes) > 0: + print >> sys.stderr, 'emcc: warning: removing duplicates in', actual + for dupe in dupes: + print >> sys.stderr, 'emcc: warning: removing duplicate', dupe + Popen([LLVM_EXTRACT, actual, '-delete', '-glob=' + dupe, '-o', actual], stderr=PIPE).communicate() + Popen([LLVM_EXTRACT, actual, '-delete', '-func=' + dupe, '-o', actual], stderr=PIPE).communicate() + Popen([LLVM_EXTRACT, actual, '-delete', '-glob=.str', '-o', actual], stderr=PIPE).communicate() # garbage that appears here + seen_symbols = seen_symbols.union(symbols.defs) + + # Finish link output = Popen([LLVM_LINK] + actual_files + ['-o', target], stdout=PIPE).communicate()[0] - assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output + assert os.path.exists(target) and (output is None or 'Could not open input file' not in output), 'Linking error: ' + output + '\nemcc: If you get duplicate symbol errors, try --remove-duplicates' if temp_dir: try_delete(temp_dir) @@ -811,8 +834,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e if type(passes) == str: passes = [passes] # XXX Disable crankshaft to work around v8 bug 1895 - output, err = Popen([NODE_JS, '--nocrankshaft', JS_OPTIMIZER, filename] + passes, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate() - assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + err + '\n\n' + output + output = Popen([NODE_JS, '--nocrankshaft', JS_OPTIMIZER, filename] + passes, stdout=PIPE).communicate()[0] + assert len(output) > 0 and not output.startswith('Assertion failed'), 'Error in js optimizer: ' + output filename += '.jo.js' f = open(filename, 'w') f.write(output) @@ -827,8 +850,8 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' % { 'winfix': '' if not WINDOWS e coffee = path_from_root('tools', 'eliminator', 'node_modules', 'coffee-script', 'bin', 'coffee') eliminator = path_from_root('tools', 'eliminator', 'eliminator.coffee') input = open(filename, 'r').read() - output, err = Popen([NODE_JS, coffee, eliminator], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate(input) - assert len(output) > 0, 'Error in eliminator: ' + err + '\n\n' + output + output = Popen([NODE_JS, coffee, eliminator, filename], stdout=PIPE).communicate()[0] + assert len(output) > 0, 'Error in eliminator: ' + output filename += '.el.js' f = open(filename, 'w') f.write(output) @@ -932,3 +955,41 @@ class Cache: shutil.copyfile(creator(), cachename) return cachename +# Compression of code and data for smaller downloads +class Compression: + on = False + + @staticmethod + def compressed_name(filename): + return filename + '.compress' + + @staticmethod + def compress(filename): + execute(Compression.encoder, stdin=open(filename, 'rb'), stdout=open(Compression.compressed_name(filename), 'wb')) + + @staticmethod + def worth_it(original, compressed): + return compressed < original - 1500 # save at least one TCP packet or so + +def execute(cmd, *args, **kw): + try: + return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) + except: + if not isinstance(cmd, str): + cmd = ' '.join(cmd) + print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>' + raise + +def suffix(name): + parts = name.split('.') + if len(parts) > 1: + return parts[-1] + else: + return None + +def unsuffixed(name): + return '.'.join(name.split('.')[:-1]) + +def unsuffixed_basename(name): + return os.path.basename(unsuffixed(name)) + |