summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2016-10-30 09:00:45 -0300
committerJuan Linietsky <reduzio@gmail.com>2016-10-30 09:00:45 -0300
commitab4126f51061277e87b41c48b40e7b54942d4eca (patch)
treec58168b60323c4d43b58743b099e562a89e60a56 /modules
parent8b15b26eedad4fdd33d50f5f9aa0fcc1875d503f (diff)
parent914015f3b63dd956e72ea937d46ea4b2db005ada (diff)
Merge branch 'master' of https://github.com/godotengine/godot
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub2
-rw-r--r--modules/chibi/SCsub9
-rw-r--r--modules/chibi/config.py6
-rw-r--r--modules/chibi/cp_config.h52
-rw-r--r--modules/chibi/cp_envelope.cpp369
-rw-r--r--modules/chibi/cp_envelope.h129
-rw-r--r--modules/chibi/cp_file_access_wrapper.h96
-rw-r--r--modules/chibi/cp_instrument.cpp344
-rw-r--r--modules/chibi/cp_instrument.h219
-rw-r--r--modules/chibi/cp_loader.h64
-rw-r--r--modules/chibi/cp_loader_it.cpp216
-rw-r--r--modules/chibi/cp_loader_it.h125
-rw-r--r--modules/chibi/cp_loader_it_info.cpp268
-rw-r--r--modules/chibi/cp_loader_it_instruments.cpp224
-rw-r--r--modules/chibi/cp_loader_it_patterns.cpp166
-rw-r--r--modules/chibi/cp_loader_it_samples.cpp620
-rw-r--r--modules/chibi/cp_loader_mod.cpp482
-rw-r--r--modules/chibi/cp_loader_mod.h52
-rw-r--r--modules/chibi/cp_loader_s3m.cpp413
-rw-r--r--modules/chibi/cp_loader_s3m.h111
-rw-r--r--modules/chibi/cp_loader_xm.cpp752
-rw-r--r--modules/chibi/cp_loader_xm.h89
-rw-r--r--modules/chibi/cp_mixer.h115
-rw-r--r--modules/chibi/cp_note.h102
-rw-r--r--modules/chibi/cp_order.h43
-rw-r--r--modules/chibi/cp_pattern.cpp574
-rw-r--r--modules/chibi/cp_pattern.h94
-rw-r--r--modules/chibi/cp_player_data.cpp151
-rw-r--r--modules/chibi/cp_player_data.h582
-rw-r--r--modules/chibi/cp_player_data_control.cpp324
-rw-r--r--modules/chibi/cp_player_data_effects.cpp1232
-rw-r--r--modules/chibi/cp_player_data_envelopes.cpp89
-rw-r--r--modules/chibi/cp_player_data_events.cpp679
-rw-r--r--modules/chibi/cp_player_data_filter.cpp89
-rw-r--r--modules/chibi/cp_player_data_nna.cpp144
-rw-r--r--modules/chibi/cp_player_data_notes.cpp345
-rw-r--r--modules/chibi/cp_player_data_utils.cpp138
-rw-r--r--modules/chibi/cp_sample.cpp203
-rw-r--r--modules/chibi/cp_sample.h112
-rw-r--r--modules/chibi/cp_sample_defs.h97
-rw-r--r--modules/chibi/cp_sample_manager.cpp78
-rw-r--r--modules/chibi/cp_sample_manager.h99
-rw-r--r--modules/chibi/cp_song.cpp957
-rw-r--r--modules/chibi/cp_song.h261
-rw-r--r--modules/chibi/cp_tables.cpp254
-rw-r--r--modules/chibi/cp_tables.h67
-rw-r--r--modules/chibi/event_stream_chibi.cpp872
-rw-r--r--modules/chibi/event_stream_chibi.h314
-rw-r--r--modules/chibi/register_types.cpp41
-rw-r--r--modules/chibi/register_types.h30
-rw-r--r--modules/cscript/SCsub4
-rw-r--r--modules/dds/SCsub8
-rw-r--r--modules/dds/config.py6
-rw-r--r--modules/dds/register_types.cpp44
-rw-r--r--modules/dds/register_types.h30
-rw-r--r--modules/dds/texture_loader_dds.cpp485
-rw-r--r--modules/dds/texture_loader_dds.h46
-rw-r--r--modules/enet/SCsub30
-rw-r--r--modules/enet/callbacks.c53
-rw-r--r--modules/enet/compress.c654
-rw-r--r--modules/enet/config.py7
-rw-r--r--modules/enet/enet/callbacks.h27
-rw-r--r--modules/enet/enet/enet.h596
-rw-r--r--modules/enet/enet/list.h43
-rw-r--r--modules/enet/enet/protocol.h198
-rw-r--r--modules/enet/enet/time.h18
-rw-r--r--modules/enet/enet/types.h13
-rw-r--r--modules/enet/enet/unix.h47
-rw-r--r--modules/enet/enet/utility.h12
-rw-r--r--modules/enet/enet/win32.h57
-rw-r--r--modules/enet/host.c492
-rw-r--r--modules/enet/list.c75
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp82
-rw-r--r--modules/enet/networked_multiplayer_enet.h35
-rw-r--r--modules/enet/packet.c165
-rw-r--r--modules/enet/peer.c1004
-rw-r--r--modules/enet/protocol.c1914
-rw-r--r--modules/enet/unix.c616
-rw-r--r--modules/enet/win32.c435
-rw-r--r--modules/etc1/SCsub20
-rw-r--r--modules/etc1/config.py6
-rw-r--r--modules/etc1/image_etc.cpp201
-rw-r--r--modules/etc1/image_etc.h35
-rw-r--r--modules/etc1/register_types.cpp47
-rw-r--r--modules/etc1/register_types.h30
-rw-r--r--modules/etc1/texture_loader_pkm.cpp84
-rw-r--r--modules/etc1/texture_loader_pkm.h18
-rw-r--r--modules/freetype/SCsub90
-rw-r--r--modules/freetype/config.py6
-rw-r--r--modules/freetype/register_types.cpp33
-rw-r--r--modules/freetype/register_types.h30
-rw-r--r--modules/freetype/winrtdef.h32
-rw-r--r--modules/gdscript/SCsub4
-rw-r--r--modules/gdscript/gd_compiler.cpp6
-rw-r--r--modules/gdscript/gd_functions.cpp18
-rw-r--r--modules/gdscript/gd_functions.h1
-rw-r--r--modules/gdscript/gd_parser.cpp42
-rw-r--r--modules/gdscript/gd_tokenizer.cpp12
-rw-r--r--modules/gridmap/SCsub6
-rw-r--r--modules/ik/SCsub6
-rw-r--r--modules/jpg/SCsub20
-rw-r--r--modules/jpg/config.py6
-rw-r--r--modules/jpg/image_loader_jpegd.cpp144
-rw-r--r--modules/jpg/image_loader_jpegd.h49
-rw-r--r--modules/jpg/register_types.cpp44
-rw-r--r--modules/jpg/register_types.h30
-rw-r--r--modules/mpc/SCsub28
-rw-r--r--modules/mpc/audio_stream_mpc.cpp414
-rw-r--r--modules/mpc/audio_stream_mpc.h146
-rw-r--r--modules/mpc/config.py6
-rw-r--r--modules/mpc/register_types.cpp45
-rw-r--r--modules/mpc/register_types.h30
-rw-r--r--modules/ogg/SCsub21
-rw-r--r--modules/ogg/config.py6
-rw-r--r--modules/ogg/register_types.cpp35
-rw-r--r--modules/ogg/register_types.h30
-rw-r--r--modules/openssl/SCsub687
-rw-r--r--modules/openssl/config.py6
-rw-r--r--modules/openssl/curl_hostcheck.c217
-rw-r--r--modules/openssl/curl_hostcheck.h39
-rw-r--r--modules/openssl/register_types.cpp42
-rw-r--r--modules/openssl/register_types.h30
-rw-r--r--modules/openssl/stream_peer_openssl.cpp646
-rw-r--r--modules/openssl/stream_peer_openssl.h109
-rw-r--r--modules/opus/SCsub215
-rw-r--r--modules/opus/audio_stream_opus.cpp376
-rw-r--r--modules/opus/audio_stream_opus.h141
-rw-r--r--modules/opus/config.py6
-rw-r--r--modules/opus/register_types.cpp45
-rw-r--r--modules/opus/register_types.h30
-rw-r--r--modules/pbm/SCsub8
-rw-r--r--modules/pbm/bitmap_loader_pbm.cpp252
-rw-r--r--modules/pbm/bitmap_loader_pbm.h50
-rw-r--r--modules/pbm/config.py6
-rw-r--r--modules/pbm/register_types.cpp44
-rw-r--r--modules/pbm/register_types.h30
-rw-r--r--modules/pvr/SCsub24
-rw-r--r--modules/pvr/config.py6
-rw-r--r--modules/pvr/register_types.cpp44
-rw-r--r--modules/pvr/register_types.h30
-rw-r--r--modules/pvr/texture_loader_pvr.cpp711
-rw-r--r--modules/pvr/texture_loader_pvr.h50
-rw-r--r--modules/regex/SCsub7
-rw-r--r--modules/regex/config.py8
-rw-r--r--modules/regex/regex.cpp1502
-rw-r--r--modules/regex/regex.h114
-rw-r--r--modules/regex/register_types.cpp43
-rw-r--r--modules/regex/register_types.h31
-rw-r--r--modules/squish/SCsub29
-rw-r--r--modules/squish/config.py10
-rw-r--r--modules/squish/image_compress_squish.cpp92
-rw-r--r--modules/squish/image_compress_squish.h36
-rw-r--r--modules/squish/register_types.cpp42
-rw-r--r--modules/squish/register_types.h32
-rw-r--r--modules/theora/SCsub83
-rw-r--r--modules/theora/config.py6
-rw-r--r--modules/theora/register_types.cpp45
-rw-r--r--modules/theora/register_types.h30
-rw-r--r--modules/theora/video_stream_theora.cpp940
-rw-r--r--modules/theora/video_stream_theora.h199
-rw-r--r--modules/theora/yuv2rgb.h1121
-rw-r--r--modules/visual_script/SCsub4
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp17
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h1
-rw-r--r--modules/vorbis/SCsub49
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp430
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h142
-rw-r--r--modules/vorbis/config.py6
-rw-r--r--modules/vorbis/register_types.cpp45
-rw-r--r--modules/vorbis/register_types.h30
-rw-r--r--modules/webp/SCsub121
-rw-r--r--modules/webp/config.py6
-rw-r--r--modules/webp/image_loader_webp.cpp183
-rw-r--r--modules/webp/image_loader_webp.h49
-rw-r--r--modules/webp/register_types.cpp44
-rw-r--r--modules/webp/register_types.h30
176 files changed, 24775 insertions, 6467 deletions
diff --git a/modules/SCsub b/modules/SCsub
index f37c3a55c7..4084248086 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
Import('env')
env_modules = env.Clone()
diff --git a/modules/chibi/SCsub b/modules/chibi/SCsub
new file mode 100644
index 0000000000..dffd966753
--- /dev/null
+++ b/modules/chibi/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_chibi = env_modules.Clone()
+
+# Godot source files
+env_chibi.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/chibi/config.py b/modules/chibi/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/chibi/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/chibi/cp_config.h b/modules/chibi/cp_config.h
new file mode 100644
index 0000000000..2ad704ace7
--- /dev/null
+++ b/modules/chibi/cp_config.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* cp_config.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_CONFIG_H
+#define CP_CONFIG_H
+
+
+#include "typedefs.h"
+#include "error_macros.h"
+#include "math_funcs.h"
+#include "os/memory.h"
+#include "os/copymem.h"
+
+#define CP_PRINTERR(m_err) ERR_PRINT(m_err)
+#define CP_ERR_COND(m_cond) ERR_FAIL_COND(m_cond)
+#define CP_ERR_COND_V(m_cond,m_ret) ERR_FAIL_COND_V(m_cond,m_ret)
+#define CP_FAIL_INDEX(m_index,m_size) ERR_FAIL_INDEX(m_index,m_size)
+#define CP_FAIL_INDEX_V(m_index,m_size,m_ret) ERR_FAIL_INDEX_V(m_index,m_size,m_ret)
+#define cp_intabs(m_val) ABS(m_val)
+
+#define CP_ALLOC(m_mem) memalloc(m_mem)
+#define CP_REALLOC(m_mem,m_size) memrealloc(m_mem,m_size)
+#define CP_FREE(m_mem) memfree(m_mem)
+
+#define cp_memzero(m_mem,m_size) zeromem(m_mem,m_size)
+
+#endif // CP_CONFIG_H
diff --git a/modules/chibi/cp_envelope.cpp b/modules/chibi/cp_envelope.cpp
new file mode 100644
index 0000000000..9892b6d4b0
--- /dev/null
+++ b/modules/chibi/cp_envelope.cpp
@@ -0,0 +1,369 @@
+/*************************************************************************/
+/* cp_envelope.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_envelope.h"
+
+
+CPEnvelope::CPEnvelope() {
+
+
+ reset();
+}
+
+void CPEnvelope::reset() {
+
+
+
+ on=false;
+ carry=false;
+ loop_on=false;
+ loop_begin_node=0;
+ loop_end_node=0;
+ sustain_loop_on=false;
+ sustain_loop_begin_node=0;
+ sustain_loop_end_node=0;
+ node_count=0;
+
+}
+
+int CPEnvelope::get_height_at_pos(int pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return begin_y+((pos-begin_x)*(end_y-begin_y))/(xdif?xdif:1);
+}
+
+/*
+int CPEnvelope::get_fx_height_at_pos(int pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value<<FX_HEIGHT_BITS;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value<<FX_HEIGHT_BITS;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return (begin_y<<FX_HEIGHT_BITS)+((pos-begin_x)*(end_y-begin_y)*(int)(1<<FX_HEIGHT_BITS))/(xdif?xdif:1);
+}
+*/
+
+float CPEnvelope::get_interp_height_at_pos(float pos) {
+
+ if (node_count && pos>node[node_count-1].tick_offset)
+ return node[node_count-1].value;
+
+ int begin_x,begin_y;
+ int end_x,end_y,xdif;
+ int count=0;
+ int limit=-1;
+
+ if (node_count<2) return NO_POINT;
+
+ while ((count<node_count) && (limit==-1)) {
+
+ if (node[count].tick_offset>=pos) limit=count;
+ count++;
+ }
+
+ if (pos==0) return node[0].value;
+
+ if (limit==-1) return NO_POINT;
+
+ begin_x=node[limit-1].tick_offset;
+ end_x=node[limit].tick_offset;
+ begin_y=node[limit-1].value;
+ end_y=node[limit].value;
+
+ xdif=end_x-begin_x;
+ return begin_y+((pos-begin_x)*(end_y-begin_y))/(xdif?xdif:1);
+}
+
+void CPEnvelope::set_position(int p_node,int p_x,int p_y) {
+
+ if (p_node>=node_count) return;
+
+
+
+ if (p_node==0) {
+
+ p_x=0;
+
+ } else if (p_x<=node[p_node-1].tick_offset) {
+
+ p_x=node[p_node-1].tick_offset+1;
+
+ } else if ((p_node<(node_count-1)) && (p_x>=node[p_node+1].tick_offset)) {
+
+ p_x=node[p_node+1].tick_offset-1;
+ }
+
+ if (p_x>=9999) p_x=9999;
+
+ if (p_y>max_value) p_y=max_value;
+ if (p_y<min_value) p_y=min_value;
+
+
+ node[p_node].tick_offset=p_x;
+ node[p_node].value=p_y;
+
+
+
+}
+
+int CPEnvelope::add_position(int p_x,int p_y,bool p_move_loops) {
+
+ if (node_count==MAX_POINTS) return -1;
+
+
+ int i,new_node;
+
+ // if this is assigning an existing node, let's quit.
+ for (i=0;i<node_count;i++) if (p_x==node[i].tick_offset) return -1;
+
+
+ i=0;
+ while ((i<node_count) && (p_x>=node[i].tick_offset)) i++;
+
+ new_node=i;
+ node_count++;
+
+ if (p_move_loops) {
+ if (loop_begin_node>=new_node) loop_begin_node++;
+ if (loop_end_node>=new_node) loop_end_node++;
+ if (sustain_loop_begin_node>=new_node) sustain_loop_begin_node++;
+ if (sustain_loop_end_node>=new_node) sustain_loop_end_node++;
+ }
+ for (i=node_count-1;i>new_node;i--) node[i]=node[i-1];
+
+
+
+ set_position(new_node,p_x,p_y);
+
+
+
+ return new_node;
+
+}
+
+void CPEnvelope::set_loop_begin(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ loop_begin_node=pos;
+
+ if (loop_end_node<loop_begin_node) loop_end_node=loop_begin_node;
+
+
+
+}
+
+void CPEnvelope::set_loop_end(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ loop_end_node=pos;
+
+ if (loop_end_node<loop_begin_node) loop_begin_node=loop_end_node;
+
+
+
+
+}
+
+
+void CPEnvelope::set_sustain_loop_begin(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ sustain_loop_begin_node=pos;
+
+ if (sustain_loop_end_node<sustain_loop_begin_node) sustain_loop_end_node=sustain_loop_begin_node;
+
+
+
+}
+
+void CPEnvelope::set_sustain_loop_end(int pos) {
+
+ if ((pos<0) || (pos>=node_count)) return;
+
+
+
+ sustain_loop_end_node=pos;
+
+ if (sustain_loop_end_node<sustain_loop_begin_node) sustain_loop_begin_node=sustain_loop_end_node;
+
+
+
+}
+
+void CPEnvelope::set_loop_enabled(bool p_enabled) {
+
+ loop_on=p_enabled;
+}
+bool CPEnvelope::is_loop_enabled() {
+
+ return loop_on;
+}
+
+
+void CPEnvelope::set_sustain_loop_enabled(bool p_enabled) {
+
+ sustain_loop_on=p_enabled;
+}
+bool CPEnvelope::is_sustain_loop_enabled() {
+
+ return sustain_loop_on;
+}
+
+void CPEnvelope::del_position(int p_node) {
+
+ if ((node_count<3) || (p_node<=0) || (p_node>=node_count)) return;
+
+
+
+ int i;
+
+ if (loop_begin_node>=p_node) loop_begin_node--;
+ if (loop_end_node>=p_node) loop_end_node--;
+ if (sustain_loop_begin_node>=p_node) sustain_loop_begin_node--;
+ if (sustain_loop_end_node>=p_node) sustain_loop_end_node--;
+
+ for (i=p_node;i<node_count-1;i++) node[i]=node[i+1];
+
+ node_count--;
+
+
+
+}
+
+uint8_t CPEnvelope::get_loop_begin() {
+
+
+ return loop_begin_node;
+}
+uint8_t CPEnvelope::get_loop_end() {
+
+ return loop_end_node;
+}
+
+uint8_t CPEnvelope::get_sustain_loop_begin() {
+
+
+ return sustain_loop_begin_node;
+}
+uint8_t CPEnvelope::get_sustain_loop_end() {
+
+ return sustain_loop_end_node;
+}
+
+
+
+void CPEnvelope::set_enabled(bool p_enabled) {
+
+ on=p_enabled;
+}
+
+bool CPEnvelope::is_enabled() {
+
+ return on;
+}
+
+void CPEnvelope::set_carry_enabled(bool p_enabled) {
+
+ carry=p_enabled;
+}
+bool CPEnvelope::is_carry_enabled() {
+
+ return carry;
+}
+
+uint8_t CPEnvelope::get_node_count() {
+
+ return node_count;
+}
+
+const CPEnvelope::Point& CPEnvelope::get_node(int p_idx) {
+
+ if (p_idx<0 || p_idx>=node_count)
+ return node[node_count-1];
+
+ return node[p_idx];
+
+}
+
+
diff --git a/modules/chibi/cp_envelope.h b/modules/chibi/cp_envelope.h
new file mode 100644
index 0000000000..d1ada53f7d
--- /dev/null
+++ b/modules/chibi/cp_envelope.h
@@ -0,0 +1,129 @@
+/*************************************************************************/
+/* cp_envelope.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_ENVELOPE_H
+#define CP_ENVELOPE_H
+
+#include "cp_config.h"
+
+/**envelope?
+ *@author Juan Linietsky
+ */
+
+/******************************
+ envelope.h
+ ----------
+
+Proovides an envelope, and basic functions
+for it that can be used for both player
+and interface
+********************************/
+
+
+class CPEnvelope {
+ enum {
+
+ MAX_POINTS=25
+ };
+
+ struct Point {
+
+ uint16_t tick_offset;
+ int16_t value;
+ };
+
+ Point node[MAX_POINTS];
+
+ int8_t node_count;
+
+ bool on;
+ bool carry;
+
+ bool loop_on;
+
+ uint8_t loop_begin_node;
+ uint8_t loop_end_node;
+
+ bool sustain_loop_on;
+ uint8_t sustain_loop_begin_node;
+ uint8_t sustain_loop_end_node;
+
+
+ int8_t max_value;
+ int8_t min_value;
+
+
+public:
+ enum {
+
+ NO_POINT=-5000,
+ };
+
+ void set_max(int8_t p_max) { max_value=p_max; }
+ int8_t get_max() { return max_value; }
+ void set_min(int8_t p_min) { min_value=p_min; }
+ int8_t get_min() { return min_value; }
+
+ uint8_t get_node_count();
+ const Point& get_node(int p_idx);
+
+ void set_position(int p_node,int p_x,int p_y);
+ int add_position(int p_x,int p_y,bool p_move_loops=true);
+ void del_position(int p_node);
+
+ void set_loop_enabled(bool p_enabled);
+ bool is_loop_enabled();
+ void set_loop_begin(int pos);
+ void set_loop_end(int pos);
+ uint8_t get_loop_begin();
+ uint8_t get_loop_end();
+
+ void set_sustain_loop_enabled(bool p_enabled);
+ bool is_sustain_loop_enabled();
+ void set_sustain_loop_begin(int pos);
+ void set_sustain_loop_end(int pos);
+ uint8_t get_sustain_loop_begin();
+ uint8_t get_sustain_loop_end();
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled();
+
+ void set_carry_enabled(bool p_enabled);
+ bool is_carry_enabled();
+
+ void reset();
+ int get_height_at_pos(int pos);
+ float get_interp_height_at_pos(float pos);
+
+
+ CPEnvelope();
+
+};
+
+#endif
diff --git a/modules/chibi/cp_file_access_wrapper.h b/modules/chibi/cp_file_access_wrapper.h
new file mode 100644
index 0000000000..5b361c0ea8
--- /dev/null
+++ b/modules/chibi/cp_file_access_wrapper.h
@@ -0,0 +1,96 @@
+/*************************************************************************/
+/* cp_file_access_wrapper.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_FILE_ACCESS_WRAPPER_H
+#define CP_FILE_ACCESS_WRAPPER_H
+
+#include "cp_config.h"
+
+class CPFileAccessWrapper {
+public:
+
+ enum ModeFlags {
+
+ READ=1,
+ WRITE=2,
+ READ_WRITE=3,
+ };
+
+ enum Error {
+
+ OK,
+ ERROR_FILE_NOT_FOUND,
+ ERROR_FILE_BAD_DRIVE,
+ ERROR_FILE_BAD_PATH,
+ ERROR_FILE_NO_PERMISSION,
+ ERROR_ALREADY_IN_USE,
+ ERROR_INVALID_PARAMETERS,
+ ERROR_OPENING_FILE,
+ ERROR_READING_FILE,
+ ERROR_WRITING_FILE
+ };
+
+ virtual Error open(const char *p_filename, int p_mode_flags)=0;
+ virtual void close()=0;
+
+ virtual void seek(uint32_t p_position)=0;
+ virtual void seek_end()=0;
+ virtual uint32_t get_pos()=0;
+
+ virtual bool eof_reached()=0;
+
+ virtual uint8_t get_byte()=0;
+ virtual void get_byte_array(uint8_t *p_dest,int p_elements)=0;
+ virtual void get_word_array(uint16_t *p_dest,int p_elements)=0;
+
+ virtual uint16_t get_word()=0;
+ virtual uint32_t get_dword()=0;
+
+ // use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
+ // It's not about the current CPU type but file formats.
+ // this flags get reset to false (little endian) on each open
+ virtual void set_endian_conversion(bool p_swap)=0;
+ virtual bool is_open()=0;
+
+ virtual Error get_error()=0;
+
+ virtual void store_byte(uint8_t p_dest)=0;
+ virtual void store_byte_array(const uint8_t *p_dest,int p_elements)=0;
+
+ virtual void store_word(uint16_t p_dest)=0;
+ virtual void store_dword(uint32_t p_dest)=0;
+
+
+
+ virtual ~CPFileAccessWrapper(){}
+
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_instrument.cpp b/modules/chibi/cp_instrument.cpp
new file mode 100644
index 0000000000..7a732e33a4
--- /dev/null
+++ b/modules/chibi/cp_instrument.cpp
@@ -0,0 +1,344 @@
+/*************************************************************************/
+/* cp_instrument.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_instrument.h"
+#include "cp_song.h"
+#include "cp_note.h"
+
+
+
+const char *CPInstrument::get_name() {
+
+ return name;
+
+}
+void CPInstrument::set_name(const char *p_name) {
+
+
+ if (p_name==NULL) {
+ name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<MAX_NAME_LEN;i++) {
+
+
+ name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ name[MAX_NAME_LEN-1]=0; /* just in case */
+
+
+}
+
+void CPInstrument::set_sample_number(uint8_t p_note,uint8_t p_sample_id) {
+
+ CP_ERR_COND(p_note>=CPNote::NOTES);
+ CP_ERR_COND(p_sample_id>CPSong::MAX_SAMPLES && p_sample_id!=CPNote::EMPTY);
+ data.sample_number[p_note]=p_sample_id;
+
+
+}
+uint8_t CPInstrument::get_sample_number(uint8_t p_note) {
+
+ CP_ERR_COND_V(p_note>=CPNote::NOTES,0);
+ return data.sample_number[p_note];
+}
+
+void CPInstrument::set_note_number(uint8_t p_note,uint8_t p_note_id) {
+
+ CP_ERR_COND(p_note>=CPNote::NOTES);
+ CP_ERR_COND(p_note_id>=CPNote::NOTES && p_note_id!=CPNote::EMPTY);
+ data.note_number[p_note]=p_note_id;
+
+}
+uint8_t CPInstrument::get_note_number(uint8_t p_note) {
+
+ CP_ERR_COND_V(p_note>=CPNote::NOTES,0);
+ return data.note_number[p_note];
+
+}
+
+void CPInstrument::set_NNA_type(NNA_Type p_NNA_type) {
+
+ data.NNA_type=p_NNA_type;
+}
+CPInstrument::NNA_Type CPInstrument::get_NNA_type() {
+
+ return data.NNA_type;
+}
+
+void CPInstrument::set_DC_type(DC_Type p_DC_type) {
+
+ data.DC_type=p_DC_type;
+}
+CPInstrument::DC_Type CPInstrument::get_DC_type() {
+
+ return data.DC_type;
+
+}
+
+void CPInstrument::set_DC_action(DC_Action p_DC_action) {
+
+ data.DC_action=p_DC_action;
+}
+CPInstrument::DC_Action CPInstrument::get_DC_action() {
+
+ return data.DC_action;
+}
+
+/* Volume */
+
+void CPInstrument::set_volume_global_amount(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_VOLUME);
+ data.volume.global_amount=p_amount;
+
+}
+uint8_t CPInstrument::get_volume_global_amount() {
+
+ return data.volume.global_amount;
+}
+
+void CPInstrument::set_volume_fadeout(uint16_t p_amount) {
+ CP_ERR_COND(p_amount>MAX_FADEOUT);
+ data.volume.fadeout=p_amount;
+}
+uint16_t CPInstrument::get_volume_fadeout() {
+
+ return data.volume.fadeout;
+}
+void CPInstrument::set_volume_random_variation(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_VOLUME_RANDOM);
+ data.volume.random_variation=p_amount;
+}
+uint8_t CPInstrument::get_volume_random_variation() {
+
+ return data.volume.random_variation;
+}
+
+/* Panning */
+
+void CPInstrument::set_pan_default_amount(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_PAN);
+ data.pan.default_amount=p_amount;
+}
+uint8_t CPInstrument::get_pan_default_amount() {
+
+ return data.pan.default_amount;
+}
+
+void CPInstrument::set_pan_default_enabled(bool p_enabled) {
+
+ data.pan.use_default=p_enabled;
+}
+bool CPInstrument::is_pan_default_enabled() {
+
+ return data.pan.use_default;
+
+}
+
+void CPInstrument::set_pan_pitch_separation(int8_t p_amount) {
+
+ CP_ERR_COND(p_amount<-32);
+ CP_ERR_COND(p_amount>32);
+ data.pan.pitch_separation=p_amount;
+}
+int8_t CPInstrument::get_pan_pitch_separation() {
+
+ return data.pan.pitch_separation;
+}
+
+void CPInstrument::set_pan_pitch_center(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>=CPNote::NOTES);
+ data.pan.pitch_center=p_amount;
+}
+uint8_t CPInstrument::get_pan_pitch_center() {
+
+ return data.pan.pitch_center;
+}
+
+void CPInstrument::set_pan_random_variation(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_PAN_RANDOM);
+ data.pan.random_variation=p_amount;
+}
+uint8_t CPInstrument::get_pan_random_variation() {
+
+ return data.pan.random_variation;
+}
+
+/* Pitch / Filter */
+
+void CPInstrument::set_pitch_use_as_filter(bool p_enabled) {
+
+ data.pitch.use_as_filter=p_enabled;
+}
+bool CPInstrument::is_pitch_use_as_filter() {
+
+ return data.pitch.use_as_filter;
+}
+
+void CPInstrument::set_filter_use_default_cutoff(bool p_enabled) {
+
+ data.pitch.use_default_cutoff=p_enabled;
+
+}
+bool CPInstrument::filter_use_default_cutoff() {
+
+ return data.pitch.use_default_cutoff;
+}
+
+void CPInstrument::set_filter_default_cutoff(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_FILTER_CUTOFF);
+ data.pitch.default_cutoff=p_amount;
+}
+uint8_t CPInstrument::get_filter_default_cutoff() {
+
+ return data.pitch.default_cutoff;
+}
+
+void CPInstrument::set_filter_use_default_resonance(bool p_enabled) {
+
+ data.pitch.use_default_resonance=p_enabled;
+}
+bool CPInstrument::filter_use_default_resonance() {
+
+ return data.pitch.use_default_resonance;
+}
+
+void CPInstrument::set_filter_default_resonance(uint8_t p_amount) {
+
+ CP_ERR_COND(p_amount>MAX_FILTER_RESONANCE);
+ data.pitch.default_resonance=p_amount;
+
+}
+uint8_t CPInstrument::get_filter_default_resonance() {
+
+ return data.pitch.default_resonance;
+}
+
+/* Envelopes */
+
+
+CPEnvelope* CPInstrument::get_volume_envelope() {
+
+ return &data.volume.envelope;
+}
+CPEnvelope* CPInstrument::get_pan_envelope() {
+
+ return &data.pan.envelope;
+}
+CPEnvelope* CPInstrument::get_pitch_filter_envelope() {
+
+ return &data.pitch.envelope;
+
+
+}
+
+
+void CPInstrument::reset() {
+
+ name[0]=0;
+
+ data.NNA_type=NNA_NOTE_CUT;
+ data.DC_action=DCA_NOTE_CUT;
+ data.DC_type=DCT_DISABLED;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ data.sample_number[i]=CPNote::EMPTY;
+ data.note_number[i]=i;
+ }
+
+ data.volume.envelope.reset();
+ data.volume.envelope.set_max(64);
+ data.volume.envelope.set_min(0);
+ data.volume.envelope.add_position(0,64,false);
+ data.volume.envelope.add_position(30,64,false);
+
+ data.volume.global_amount=MAX_VOLUME;
+ data.volume.fadeout=0;
+ data.volume.random_variation=0;
+
+ data.pan.envelope.reset();
+ data.pan.envelope.set_max(32);
+ data.pan.envelope.set_min(-32);
+ data.pan.envelope.add_position(0,0,false);
+ data.pan.envelope.add_position(30,0,false);
+
+ data.pan.default_amount=32;
+ data.pan.pitch_center=48;
+ data.pan.pitch_separation=0;
+ data.pan.use_default=false;
+ data.pan.random_variation=0;
+
+
+ data.pitch.envelope.reset();
+ data.pitch.envelope.set_max(32);
+ data.pitch.envelope.set_min(-32);
+ data.pitch.envelope.add_position(0,0,false);
+ data.pitch.envelope.add_position(30,0,false);
+ data.pitch.use_as_filter=false;
+ data.pitch.use_default_cutoff=false;
+ data.pitch.use_default_resonance=false;
+ data.pitch.default_cutoff=0;
+ data.pitch.default_resonance=0;
+
+}
+
+bool CPInstrument::is_empty() {
+
+ bool has_sample=false;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ if (data.sample_number[i]!=CPNote::EMPTY) {
+
+ has_sample=true;
+ break;
+ }
+ }
+
+ return !has_sample;
+}
+
+CPInstrument::CPInstrument() {
+
+ reset();
+
+}
+
diff --git a/modules/chibi/cp_instrument.h b/modules/chibi/cp_instrument.h
new file mode 100644
index 0000000000..d8eb8333ee
--- /dev/null
+++ b/modules/chibi/cp_instrument.h
@@ -0,0 +1,219 @@
+/*************************************************************************/
+/* cp_instrument.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_INSTRUMENT_H
+#define CP_INSTRUMENT_H
+
+
+#include "cp_config.h"
+#include "cp_note.h"
+#include "cp_envelope.h"
+
+class CPInstrument {
+public:
+
+
+ enum NNA_Type {
+
+ NNA_NOTE_CUT,
+ NNA_NOTE_CONTINUE,
+ NNA_NOTE_OFF,
+ NNA_NOTE_FADE
+ };
+
+ enum DC_Type {
+
+ DCT_DISABLED,
+ DCT_NOTE,
+ DCT_SAMPLE,
+ DCT_INSTRUMENT
+ };
+
+ enum DC_Action
+ {
+
+ DCA_NOTE_CUT,
+ DCA_NOTE_OFF,
+ DCA_NOTE_FADE,
+ };
+
+ enum EnvelopeType {
+ VOLUME_ENVELOPE,
+ PAN_ENVELOPE,
+ PITCH_ENVELOPE
+ };
+
+
+ enum {
+ MAX_NAME_LEN=26,
+ MAX_ENVELOPE_NODES=25,
+ ENVELOPE_FRAC_BITS=8,
+ MAX_VOLUME=128,
+ MAX_FADEOUT=256,
+ MAX_PAN=128,
+ MAX_VOLUME_RANDOM=100,
+ MAX_PAN_RANDOM=64, //what did this guy have inside his head?
+
+ MAX_FILTER_CUTOFF=127,
+ MAX_FILTER_RESONANCE=127
+
+ };
+
+
+ struct Data {
+
+
+ uint8_t sample_number[CPNote::NOTES];
+ uint8_t note_number[CPNote::NOTES];
+
+ NNA_Type NNA_type;
+ DC_Type DC_type;
+ DC_Action DC_action;
+
+ struct Volume {
+
+ CPEnvelope envelope;
+ uint8_t global_amount;
+ uint16_t fadeout;
+ uint8_t random_variation;
+
+ } volume;
+
+ struct Pan {
+
+ CPEnvelope envelope;
+ bool use_default;
+ uint8_t default_amount;
+ int8_t pitch_separation;
+ uint8_t pitch_center;
+ uint8_t random_variation;
+
+ } pan;
+
+ struct Pitch {
+
+ CPEnvelope envelope;
+ bool use_as_filter;
+ bool use_default_cutoff;
+ uint8_t default_cutoff;
+ bool use_default_resonance;
+ uint8_t default_resonance;
+ } pitch;
+
+ };
+
+private:
+
+
+
+ Data data;
+ char name[MAX_NAME_LEN];
+
+public:
+
+ /* CPInstrument General */
+
+ const char *get_name();
+ void set_name(const char *p_name);
+
+ void set_sample_number(uint8_t p_note,uint8_t p_sample_id);
+ uint8_t get_sample_number(uint8_t p_note);
+
+ void set_note_number(uint8_t p_note,uint8_t p_note_id);
+ uint8_t get_note_number(uint8_t p_note);
+
+ void set_NNA_type(NNA_Type p_NNA_type);
+ NNA_Type get_NNA_type();
+
+ void set_DC_type(DC_Type p_DC_type);
+ DC_Type get_DC_type();
+
+ void set_DC_action(DC_Action p_DC_action);
+ DC_Action get_DC_action();
+
+ /* Volume */
+
+ void set_volume_global_amount(uint8_t p_amount);
+ uint8_t get_volume_global_amount();
+
+ void set_volume_fadeout(uint16_t p_amount);
+ uint16_t get_volume_fadeout();
+
+ void set_volume_random_variation(uint8_t p_amount);
+ uint8_t get_volume_random_variation();
+
+ /* Panning */
+
+ void set_pan_default_amount(uint8_t p_amount);
+ uint8_t get_pan_default_amount();
+
+ void set_pan_default_enabled(bool p_enabled);
+ bool is_pan_default_enabled();
+
+ void set_pan_pitch_separation(int8_t p_amount);
+ int8_t get_pan_pitch_separation();
+
+ void set_pan_pitch_center(uint8_t p_amount);
+ uint8_t get_pan_pitch_center();
+
+ void set_pan_random_variation(uint8_t p_amount);
+ uint8_t get_pan_random_variation();
+
+ /* Pitch / Filter */
+
+ void set_pitch_use_as_filter(bool p_enabled);
+ bool is_pitch_use_as_filter();
+
+ void set_filter_use_default_cutoff(bool p_enabled);
+ bool filter_use_default_cutoff();
+
+ void set_filter_default_cutoff(uint8_t p_amount);
+ uint8_t get_filter_default_cutoff();
+
+ void set_filter_use_default_resonance(bool p_enabled);
+ bool filter_use_default_resonance();
+
+ void set_filter_default_resonance(uint8_t p_amount);
+ uint8_t get_filter_default_resonance();
+
+ CPEnvelope* get_volume_envelope();
+ CPEnvelope* get_pan_envelope();
+ CPEnvelope* get_pitch_filter_envelope();
+
+ bool is_empty();
+
+ void reset();
+ CPInstrument();
+
+};
+
+
+
+#endif
+
+
diff --git a/modules/chibi/cp_loader.h b/modules/chibi/cp_loader.h
new file mode 100644
index 0000000000..9d1074d1b8
--- /dev/null
+++ b/modules/chibi/cp_loader.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* cp_loader.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_LOADER_H
+#define CP_LOADER_H
+
+
+#include "cp_song.h"
+#include "cp_file_access_wrapper.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class CPLoader {
+
+public:
+
+ enum Error {
+ FILE_OK,
+ FILE_UNRECOGNIZED,
+ FILE_CANNOT_OPEN,
+ FILE_CORRUPTED,
+ FILE_OUT_OF_MEMORY,
+ };
+
+
+ virtual bool can_load_song()=0;
+ virtual bool can_load_sample()=0;
+ virtual bool can_load_instrument()=0;
+
+ virtual Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset)=0;
+ virtual Error load_sample(const char *p_file,CPSample *p_sample)=0;
+ virtual Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx)=0;
+
+
+ virtual ~CPLoader() {}
+
+};
+
+#endif
diff --git a/modules/chibi/cp_loader_it.cpp b/modules/chibi/cp_loader_it.cpp
new file mode 100644
index 0000000000..20a3960a23
--- /dev/null
+++ b/modules/chibi/cp_loader_it.cpp
@@ -0,0 +1,216 @@
+/*************************************************************************/
+/* cp_loader_it.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+bool CPLoader_IT::can_load_song() { return true; }
+bool CPLoader_IT::can_load_sample() { return true; }
+bool CPLoader_IT::can_load_instrument() { return true; }
+
+CPLoader::Error CPLoader_IT::load_song(const char *p_file,CPSong *p_song, bool p_sampleset) {
+
+
+ song=p_song;
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+
+ Error err;
+
+ char aux_identifier[4];
+ file->get_byte_array((uint8_t*)aux_identifier,4);
+
+ if ( aux_identifier[0]!='I' ||
+ aux_identifier[1]!='M' ||
+ aux_identifier[2]!='P' ||
+ aux_identifier[3]!='M') {
+
+
+ CP_PRINTERR("IT CPLoader CPSong: Failed Identifier");
+ return FILE_UNRECOGNIZED;
+ }
+
+
+ if (p_sampleset) {
+
+ song->reset(false,true,true,false);
+
+ if ((err=load_header(true))) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_samples())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_instruments())) {
+ file->close();
+ return err;
+ }
+
+ return FILE_OK;
+ }
+
+ song->reset();
+
+ if ((err=load_header(false))) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_orders())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_patterns())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_samples())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_effects())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_instruments())) {
+ file->close();
+ return err;
+ }
+
+ if ((err=load_message())) {
+ file->close();
+ return err;
+ }
+
+ file->close();
+ return FILE_OK;
+
+}
+
+
+
+
+CPLoader::Error CPLoader_IT::load_sample(const char *p_file,CPSample *p_sample) {
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+ p_sample->reset();
+ CPLoader::Error res=load_sample(p_sample);
+
+ file->close();
+
+ return res;
+}
+CPLoader::Error CPLoader_IT::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ CP_FAIL_INDEX_V(p_instr_idx,CPSong::MAX_INSTRUMENTS,CPLoader::FILE_CANNOT_OPEN);
+
+ if (file->open( p_file, CPFileAccessWrapper::READ )!=CPFileAccessWrapper::OK)
+ return CPLoader::FILE_CANNOT_OPEN;
+
+
+ p_song->get_instrument( p_instr_idx )->reset();
+
+
+ int samples=0;
+ CPLoader::Error res=load_instrument( p_song->get_instrument( p_instr_idx ), &samples );
+
+ if (res) {
+ file->close();
+ return res;
+ }
+
+
+ char exchange[CPSong::MAX_SAMPLES];
+ for (int i=0;i<CPSong::MAX_SAMPLES;i++)
+ exchange[i]=0;
+
+ for (int i=0;i<samples;i++) {
+
+ file->seek( 554+i*80 ); //i think this should work?! seems to.. but i'm not sure
+
+ /* find free sample */
+
+ int free_idx=-1;
+ for (int s=0;s<CPSong::MAX_SAMPLES;s++) {
+
+ if (p_song->get_sample( s )->get_sample_data().is_null()) {
+ free_idx=s;
+ break;
+ }
+ }
+ if (free_idx==-1)
+ break; //can't seem to be able to load more samples
+
+ exchange[i]=free_idx;
+ res=load_sample( p_song->get_sample( free_idx ) );
+
+ if (res) {
+
+ file->close();
+ return res;
+ }
+ }
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ int smp=song->get_instrument(p_instr_idx)->get_sample_number(i);
+
+ if (smp>=CPSong::MAX_SAMPLES)
+ continue;
+
+ if (smp<0)
+ continue;
+
+ smp=exchange[smp];
+
+ song->get_instrument(p_instr_idx)->set_sample_number(i,smp);
+
+ }
+
+ file->close();
+
+ return res;
+
+}
+
+CPLoader_IT::CPLoader_IT(CPFileAccessWrapper *p_file) {
+
+ file=p_file;
+
+}
diff --git a/modules/chibi/cp_loader_it.h b/modules/chibi/cp_loader_it.h
new file mode 100644
index 0000000000..38a1cdd9c4
--- /dev/null
+++ b/modules/chibi/cp_loader_it.h
@@ -0,0 +1,125 @@
+/*************************************************************************/
+/* cp_loader_it.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_IT_H
+#define CP_LOADER_IT_H
+
+#include "cp_loader.h"
+/**
+ *@author Juan Linietsky
+ */
+
+/******************************
+ loader_it.h
+ ----------
+Impulse Tracker Module CPLoader!
+It lacks support for old
+instrument files methinks...
+and some other things like
+midi.
+********************************/
+
+class AuxSampleData; //used for internal crap
+
+class CPLoader_IT : public CPLoader {
+
+
+
+ CPFileAccessWrapper *file;
+ CPSong *song;
+
+ struct IT_Header {
+ uint8_t blank01[2];
+ uint16_t ordnum;
+ uint16_t insnum;
+ uint16_t smpnum;
+ uint16_t patnum;
+ uint16_t cwt; /* Created with tracker (y.xx = 0x0yxx) */
+ uint16_t cmwt; /* Compatible with tracker ver > than val. */
+ uint16_t flags;
+ uint16_t special; /* bit 0 set = song message attached */
+ uint16_t msglength;
+ uint32_t msgoffset;
+ bool is_chibi;
+ };
+
+ /* Variables to store temp data */
+ IT_Header header;
+
+ /* CPSong Info Methods */
+ Error load_header(bool p_dont_set);
+ Error load_orders();
+ Error load_message();
+
+ /* CPPattern Methods */
+ Error load_patterns();
+
+ /* CPSample Methods */
+
+ Error load_samples();
+ Error load_sample(CPSample *p_sample);
+ CPSample_ID load_sample_data(AuxSampleData& p_sample_data);
+
+ // CPSample decompression
+
+ uint32_t read_n_bits_from_IT_compressed_block(uint8_t p_bits_to_read);
+ bool read_IT_compressed_block (bool p_16bits);
+ void free_IT_compressed_block ();
+ bool load_sample_8bits_IT_compressed(void *p_dest_buffer,int p_buffsize);
+ bool load_sample_16bits_IT_compressed(void *p_dest_buffer,int p_buffsize);
+ uint32_t *source_buffer; /* source buffer */
+ uint32_t *source_position; /* actual reading position */
+ uint8_t source_remaining_bits; /* bits remaining in read dword */
+ uint8_t* pat_data;
+
+ /* CPInstruments Methods */
+ Error load_effects();
+ Error load_instruments();
+ Error load_instrument(CPInstrument *p_instrument,int *p_samples=0);
+ void load_envelope(CPEnvelope *p_envelope,bool*p_has_filter_flag=0);
+
+
+public:
+
+
+ bool can_load_song();
+ bool can_load_sample();
+ bool can_load_instrument();
+
+ Error load_song(const char *p_file,CPSong *p_song, bool p_sampleset=false);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+ CPLoader_IT(CPFileAccessWrapper *p_file);
+
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_loader_it_info.cpp b/modules/chibi/cp_loader_it_info.cpp
new file mode 100644
index 0000000000..0360f7f9a4
--- /dev/null
+++ b/modules/chibi/cp_loader_it_info.cpp
@@ -0,0 +1,268 @@
+/*************************************************************************/
+/* cp_loader_it_info.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+
+
+CPLoader::Error CPLoader_IT::load_header(bool p_dont_set) {
+
+
+ char aux_songname[26];
+
+ file->get_byte_array((uint8_t*)aux_songname,26);
+ if (!p_dont_set)
+ song->set_name( aux_songname );
+
+ uint8_t aux_hlmin=file->get_byte();
+ uint8_t aux_hlmaj=file->get_byte();
+
+ if (aux_hlmin==0) aux_hlmin=4;
+ if (aux_hlmaj==0) aux_hlmaj=16;
+
+ if (!p_dont_set) {
+ song->set_row_highlight_minor( aux_hlmin );
+ song->set_row_highlight_major( aux_hlmaj );
+ }
+
+ header.ordnum=file->get_word();
+ header.insnum=file->get_word();
+ header.smpnum=file->get_word();
+ header.patnum=file->get_word();
+
+ header.cwt=file->get_word(); /* Created with tracker (y.xx = 0x0yxx) */
+ header.cmwt=file->get_word(); /* Compatible with tracker ver > than val. */
+ header.flags=file->get_word();
+
+ if (!p_dont_set) {
+ song->set_stereo( header.flags & 1 );
+ song->set_linear_slides( header.flags & 8 );
+ song->set_old_effects( header.flags & 16 );
+ song->set_compatible_gxx( header.flags & 32 );
+ song->set_instruments( header.flags & 4 );
+ }
+
+
+ header.special=file->get_word();
+ if (!p_dont_set) {
+
+ song->set_global_volume( file->get_byte() );
+ song->set_mixing_volume( file->get_byte() );
+ song->set_speed( file->get_byte() );
+ song->set_tempo( file->get_byte() );
+ song->set_stereo_separation( file->get_byte() );
+
+ } else {
+
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ file->get_byte(); // skip
+ }
+ file->get_byte(); // ZERO Byte
+ header.msglength=file->get_word();
+ header.msgoffset=file->get_dword();
+ char chibi[4];
+ file->get_byte_array((uint8_t*)chibi,4);
+ header.is_chibi=(chibi[0]=='C' && chibi[1]=='H' && chibi[2]=='B' && chibi[3]=='I');
+
+ for (int i=0;i<64;i++) {
+
+ uint8_t panbyte=file->get_byte();
+
+ uint8_t pan_dst=(panbyte<65) ? panbyte : 32;
+ bool surround_dst=(panbyte==100);
+ bool mute_dst=(panbyte>=128);
+
+ if (!p_dont_set) {
+ song->set_channel_pan( i, pan_dst );
+ song->set_channel_surround( i, surround_dst );
+ song->set_channel_mute( i, mute_dst );
+ }
+ }
+ for (int i=0;i<64;i++) {
+ unsigned char cv = file->get_byte();
+ if (!p_dont_set)
+ song->set_channel_volume( i, cv );
+ }
+
+ CP_ERR_COND_V( file->eof_reached(),FILE_CORRUPTED );
+ CP_ERR_COND_V( file->get_error(),FILE_CORRUPTED );
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_IT::load_effects() {
+
+ if (!header.is_chibi)
+ return FILE_OK; //no effects, regular IT file
+
+ /* GOTO End of IT header */
+ file->seek(0xC0+header.ordnum+header.insnum*4+header.smpnum*4+header.patnum*4);
+
+
+ if (file->get_byte()>0) //not made with this version, ignore extended info
+ return FILE_OK;
+
+ /* Chibitracker Extended info */
+
+ switch(file->get_byte()) {
+
+ case CPSong::REVERB_MODE_ROOM: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_ROOM );
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_SMALL: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_SMALL );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_MEDIUM: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_MEDIUM );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_LARGE: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_STUDIO_LARGE );
+
+ } break;
+ case CPSong::REVERB_MODE_HALL: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_HALL );
+
+ } break;
+ case CPSong::REVERB_MODE_SPACE_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_SPACE_ECHO );
+
+ } break;
+
+ case CPSong::REVERB_MODE_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_DELAY: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_DELAY );
+
+ } break;
+ case CPSong::REVERB_MODE_HALF_ECHO: {
+
+ song->set_reverb_mode( CPSong::REVERB_MODE_HALF_ECHO );
+
+ } break;
+
+ }
+
+ //chorus
+ song->set_chorus_speed_hz10( file->get_byte() );
+ song->set_chorus_delay_ms( file->get_byte() );
+ song->set_chorus_depth_ms10( file->get_byte() );
+ song->set_chorus_separation_ms( file->get_byte() );
+
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+ song->set_channel_reverb(i,file->get_byte());
+ }
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+ song->set_channel_chorus(i,file->get_byte());
+ }
+
+ return FILE_OK;
+
+}
+
+CPLoader::Error CPLoader_IT::load_message() {
+
+
+ if (!(header.special & 1)) {
+
+ return FILE_OK;
+ }
+
+
+ file->seek(header.msgoffset);
+
+ //(void*)tmpmsg=malloc(header.msglength+1);
+
+ char message[8000];
+
+
+ char *tmpmsg = message;
+
+ file->get_byte_array((uint8_t*)tmpmsg,header.msglength);
+ tmpmsg[header.msglength]=0;
+
+ for (int i=0;i<header.msglength;i++) if (tmpmsg[i]=='\r') tmpmsg[i]='\n';
+
+ song->set_message(tmpmsg);
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_IT::load_orders() {
+
+ file->seek(0xC0);
+
+
+ for (int i=0;i<header.ordnum;i++) {
+
+ uint8_t aux_order=file->get_byte();
+ CPOrder order=CP_ORDER_NONE;
+
+
+ if (i>=CPSong::MAX_ORDERS)
+ continue;
+ if (aux_order==254) {
+
+ order=CP_ORDER_BREAK;
+
+ } else if (aux_order<200) {
+
+ order=aux_order;
+ //nothing!
+
+ }
+ song->set_order(i,order);
+
+ }
+
+ if (file->eof_reached() || file->get_error()) {
+
+
+ return FILE_CORRUPTED;
+
+ }
+
+ return FILE_OK;
+}
+
+
+
diff --git a/modules/chibi/cp_loader_it_instruments.cpp b/modules/chibi/cp_loader_it_instruments.cpp
new file mode 100644
index 0000000000..ccb24bd81c
--- /dev/null
+++ b/modules/chibi/cp_loader_it_instruments.cpp
@@ -0,0 +1,224 @@
+/*************************************************************************/
+/* cp_loader_it_instruments.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_it.h"
+
+enum EnvFlags {
+ ENV_ON=1,
+ ENV_LOOP=2,
+ ENV_SUSLOOP=4,
+ ENV_CARRY=8,
+ ENV_FILTER=128
+};
+
+void CPLoader_IT::load_envelope(CPEnvelope *p_envelope,bool*p_has_filter_flag) {
+
+ uint8_t flags=file->get_byte();
+ uint8_t points=file->get_byte();
+ uint8_t begin=file->get_byte();
+ uint8_t end=file->get_byte();
+ uint8_t susbegin=file->get_byte();
+ uint8_t susend=file->get_byte();
+
+ p_envelope->reset();
+
+ for (int i=0;i<25;i++) {
+
+ uint8_t height=file->get_byte();
+ int8_t &signed_height=(int8_t&)height;
+ uint16_t tick=file->get_word();
+
+ if (i>=points)
+ continue;
+ p_envelope->add_position( tick, signed_height );
+
+ }
+
+ p_envelope->set_enabled( flags & ENV_ON );
+ p_envelope->set_carry_enabled( flags & ENV_CARRY);
+
+ p_envelope->set_loop_enabled( flags & ENV_LOOP );
+ p_envelope->set_loop_begin( begin );
+ p_envelope->set_loop_end( end );
+
+ p_envelope->set_sustain_loop_enabled( flags & ENV_SUSLOOP );
+ p_envelope->set_sustain_loop_begin( susbegin );
+ p_envelope->set_sustain_loop_end( susend );
+
+ if (p_has_filter_flag)
+ *p_has_filter_flag=flags&ENV_FILTER;
+
+ file->get_byte(); //zerobyte
+
+ //fill with stuff if the envelope hass less than 2 points
+ while(p_envelope->get_node_count()<2) {
+
+ p_envelope->add_position( 30*p_envelope->get_node_count(), p_envelope->get_min()==0 ? 64 : 0, false );
+ }
+}
+
+
+CPLoader::Error CPLoader_IT::load_instrument(CPInstrument *p_instrument,int *p_samples) {
+
+
+
+ char aux_header[4];
+
+ file->get_byte_array((uint8_t*)aux_header,4);
+
+
+ if ( aux_header[0]!='I' ||
+ aux_header[1]!='M' ||
+ aux_header[2]!='P' ||
+ aux_header[3]!='I') {
+ CP_PRINTERR("IT CPLoader CPInstrument: Failed Identifier");
+
+ return FILE_UNRECOGNIZED;
+ }
+
+
+
+ // Ignore deprecated 8.3 filename field
+ for (int i=0;i<12;i++) file->get_byte();
+
+ //Ignore zerobyte
+ file->get_byte(); /* (byte) CPInstrument type (always 0) */
+
+ switch( file->get_byte() ) { /* New CPNote Action [0,1,2,3] */
+ case 0: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_CUT ) ; break;
+ case 1: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_CONTINUE ) ; break;
+ case 2: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_OFF ) ; break;
+ case 3: p_instrument->set_NNA_type( CPInstrument::NNA_NOTE_FADE ) ; break;
+ };
+ switch( file->get_byte() ) { // Duplicate Check Type
+ case 0: p_instrument->set_DC_type( CPInstrument::DCT_DISABLED ); break ;
+ case 1: p_instrument->set_DC_type( CPInstrument::DCT_NOTE ); break ;
+ case 2: p_instrument->set_DC_type( CPInstrument::DCT_SAMPLE ); break ;
+ case 3: p_instrument->set_DC_type( CPInstrument::DCT_INSTRUMENT ); break ;
+ }
+ switch( file->get_byte() ) { //Duplicate Check Action
+ case 0: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_CUT ); break ;
+ case 1: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_OFF ); break ;
+ case 2: p_instrument->set_DC_action( CPInstrument::DCA_NOTE_FADE ); break ;
+ }
+
+ int fade = file->get_word();
+ //intf("AFADE: %i\n",fade);
+ if (fade>CPInstrument::MAX_FADEOUT) //needs to be clipped because of horrible modplug doings
+ fade=CPInstrument::MAX_FADEOUT;
+
+ p_instrument->set_volume_fadeout( fade );
+ p_instrument->set_pan_pitch_separation( file->get_byte() );
+ p_instrument->set_pan_pitch_center( file->get_byte() );
+ p_instrument->set_volume_global_amount( file->get_byte() );
+ uint8_t pan=file->get_byte();
+ p_instrument->set_pan_default_amount(pan&0x7F);
+ p_instrument->set_pan_default_enabled( !(pan&0x80) );
+ p_instrument->set_volume_random_variation( file->get_byte() );
+ p_instrument->set_pan_random_variation( file->get_byte() );
+
+
+
+ file->get_word(); //empty (version)
+ uint8_t samples=file->get_byte();
+ if (p_samples)
+ *p_samples=samples;
+ file->get_byte(); //empty
+ char aux_name[26];
+ file->get_byte_array((uint8_t*)aux_name,26);
+ p_instrument->set_name(aux_name);
+
+ uint8_t cutoff=file->get_byte();
+
+ p_instrument->set_filter_default_cutoff(cutoff&0x7F);
+ p_instrument->set_filter_use_default_cutoff(cutoff&0x80);
+
+ uint8_t resonance=file->get_byte();
+
+ p_instrument->set_filter_default_resonance(resonance&0x7F);
+ p_instrument->set_filter_use_default_resonance(resonance&0x80);
+
+ file->get_dword(); //MIDI, IGNORED!
+
+ /* CPNote -> CPSample table */
+ for (uint8_t i=0;i<CPNote::NOTES;i++) {
+
+
+ uint8_t note=file->get_byte();
+ if (note>=CPNote::NOTES)
+ note=0;
+ p_instrument->set_note_number(i,note);
+
+ uint8_t samp=file->get_byte();
+ if (samp==0 || samp>99)
+ samp=CPNote::EMPTY;
+ else
+ samp--;
+
+
+ p_instrument->set_sample_number(i,samp);
+
+
+ }
+
+
+ load_envelope( p_instrument->get_volume_envelope() );
+ load_envelope( p_instrument->get_pan_envelope() );
+ bool use_as_filter;
+ load_envelope( p_instrument->get_pitch_filter_envelope(), &use_as_filter );
+ p_instrument->set_pitch_use_as_filter( use_as_filter );
+
+ return FILE_OK;
+
+}
+
+
+CPLoader::Error CPLoader_IT::load_instruments() {
+
+
+ for (int i=0;i<header.insnum;i++) {
+
+
+ file->seek(0xC0+header.ordnum+i*4);
+ uint32_t final_location=file->get_dword();
+ file->seek( final_location );
+
+ Error err=load_instrument( song->get_instrument( i ) );
+ if (err)
+ return err;
+
+ }
+
+ return FILE_OK;
+
+ if (file->eof_reached() || file->get_error())
+ return FILE_CORRUPTED;
+}
+
+
diff --git a/modules/chibi/cp_loader_it_patterns.cpp b/modules/chibi/cp_loader_it_patterns.cpp
new file mode 100644
index 0000000000..d951a91620
--- /dev/null
+++ b/modules/chibi/cp_loader_it_patterns.cpp
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* cp_loader_it_patterns.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+
+
+CPLoader::Error CPLoader_IT::load_patterns() {
+
+
+ for (int i=0;i<header.patnum;i++) {
+
+ if (i>=CPSong::MAX_PATTERNS)
+ break;
+
+ /* Position where pattern offsets are stored */
+ file->seek(0xC0+header.ordnum+header.insnum*4+header.smpnum*4+i*4);
+ uint32_t pattern_offset=file->get_dword();
+
+ if (pattern_offset==0) {
+
+ continue;
+ }
+
+ uint16_t pat_size;
+ uint16_t pat_length;
+
+ int row=0,flag,channel,j;
+ uint8_t aux_byte;
+ uint32_t reserved;
+ uint8_t chan_mask[64]; //mask cache for each
+ CPNote last_value[64]; //last value of each
+
+ for (j=0;j<64;j++) {
+
+ chan_mask[j]=0;
+ last_value[j].clear();
+ }
+
+ file->seek(pattern_offset);
+
+ pat_size=file->get_word();
+ pat_length=file->get_word();
+ reserved=file->get_dword();
+
+ song->get_pattern(i)->set_length( pat_length );
+
+ do {
+
+ aux_byte=file->get_byte();
+ flag=aux_byte;
+
+ if ( flag==0 ) {
+
+ row++;
+ } else {
+
+ channel=(flag-1) & 63;
+
+ if ( flag & 128 ) {
+
+ aux_byte=file->get_byte();
+ chan_mask[channel]=aux_byte;
+ }
+
+ CPNote note; //note used for reading
+
+ if ( chan_mask[channel]&1 ) { // read note
+
+ aux_byte=file->get_byte();
+
+ if ( aux_byte<120 )
+ note.note=aux_byte;
+ else if ( aux_byte==255 )
+ note.note=CPNote::OFF;
+ else if ( aux_byte==254 )
+ note.note=CPNote::CUT;
+
+ last_value[channel].note=note.note;
+ }
+
+
+ if ( chan_mask[channel]&2 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte<100 )
+ note.instrument=aux_byte-1;
+
+ last_value[channel].instrument=note.instrument;
+ }
+ if ( chan_mask[channel]&4 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte<213 )
+ note.volume=aux_byte;
+
+ last_value[channel].volume=note.volume;
+ }
+ if ( chan_mask[channel]&8 ) {
+
+ aux_byte=file->get_byte();
+ if ( aux_byte>0 )
+ note.command=aux_byte-1;
+
+
+ last_value[channel].command=note.command;
+
+ note.parameter=file->get_byte();
+
+ last_value[channel].parameter=note.parameter;
+ }
+
+ if ( chan_mask[channel]&16 ) {
+
+ note.note=last_value[channel].note;
+ }
+
+ if ( chan_mask[channel]&32 ) {
+
+ note.instrument=last_value[channel].instrument;
+ }
+ if ( chan_mask[channel]&64 ) {
+
+ note.volume=last_value[channel].volume;
+ }
+ if ( chan_mask[channel]&128 ) {
+
+ note.command=last_value[channel].command;
+ note.parameter=last_value[channel].parameter;
+ }
+
+ song->get_pattern(i)->set_note(channel,row,note);
+ }
+
+
+ } while(row<pat_length);
+
+ }
+
+ return FILE_OK;
+}
+
diff --git a/modules/chibi/cp_loader_it_samples.cpp b/modules/chibi/cp_loader_it_samples.cpp
new file mode 100644
index 0000000000..ced7252a6c
--- /dev/null
+++ b/modules/chibi/cp_loader_it_samples.cpp
@@ -0,0 +1,620 @@
+/*************************************************************************/
+/* cp_loader_it_samples.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_it.h"
+#include "cp_sample.h"
+
+struct AuxSampleData {
+
+
+ uint32_t fileofs;
+ uint32_t c5spd;
+ uint32_t length;
+ uint32_t loop_begin;
+ uint32_t loop_end;
+ bool loop_enabled;
+ bool pingpong_enabled;
+ bool is16bit;
+ bool stereo;
+ bool exists;
+ bool compressed;
+
+};
+
+
+enum IT_Sample_Flags {
+
+ IT_SAMPLE_EXISTS=1,
+ IT_SAMPLE_16BITS=2,
+ IT_SAMPLE_STEREO=4,
+ IT_SAMPLE_COMPRESSED=8,
+ IT_SAMPLE_LOOPED=16,
+ IT_SAMPLE_SUSTAIN_LOOPED=32,
+ IT_SAMPLE_LOOP_IS_PINGPONG=64,
+ IT_SAMPLE_SUSTAIN_LOOP_IS_PINGPONG=128
+};
+
+
+CPLoader::Error CPLoader_IT::load_sample(CPSample *p_sample) {
+
+
+ AuxSampleData aux_sample_data;
+
+ char aux_header[4];
+
+ file->get_byte_array((uint8_t*)aux_header,4);
+
+ if ( aux_header[0]!='I' ||
+ aux_header[1]!='M' ||
+ aux_header[2]!='P' ||
+ aux_header[3]!='S') {
+
+ //CP_PRINTERR("IT CPLoader CPSample: Failed Identifier");
+ return FILE_UNRECOGNIZED;
+ }
+
+
+ // Ignore deprecated 8.3 filename
+ for (int i=0;i<12;i++) file->get_byte();
+
+ file->get_byte(); //ignore zerobyte
+
+ p_sample->set_global_volume( file->get_byte() );
+
+ /* SAMPLE FLAGS */
+ uint8_t flags=file->get_byte();
+ aux_sample_data.loop_enabled=flags&IT_SAMPLE_LOOPED;
+ aux_sample_data.pingpong_enabled=flags&IT_SAMPLE_LOOP_IS_PINGPONG;
+ aux_sample_data.is16bit=flags&IT_SAMPLE_16BITS;
+ aux_sample_data.exists=flags&IT_SAMPLE_EXISTS;
+ aux_sample_data.stereo=flags&IT_SAMPLE_STEREO;
+ aux_sample_data.compressed=flags&IT_SAMPLE_COMPRESSED;
+
+ p_sample->set_default_volume(file->get_byte());
+ /* SAMPLE NAME */
+ char aux_name[26];
+ file->get_byte_array((uint8_t*)aux_name,26);
+ p_sample->set_name(aux_name);
+
+ // ??
+ uint8_t convert_flag=file->get_byte();
+ // PAN
+ uint8_t pan=file->get_byte();
+ p_sample->set_pan( pan&0x7F );
+ p_sample->set_pan_enabled( pan & 0x80 );
+
+ aux_sample_data.length=file->get_dword();
+
+
+ aux_sample_data.loop_begin= file->get_dword();
+ aux_sample_data.loop_end= file->get_dword();
+ aux_sample_data.c5spd=file->get_dword();
+ /*p_sample->data.set_sustain_loop_begin=*/file->get_dword();
+ /*p_sample->data.sustain_loop_end=*/file->get_dword();
+ aux_sample_data.fileofs=file->get_dword();
+ p_sample->set_vibrato_speed( file->get_byte() );
+ p_sample->set_vibrato_depth( file->get_byte() );
+ p_sample->set_vibrato_rate( file->get_byte() );
+ switch( file->get_byte() ) {
+ /* Vibrato Wave: 0=sine, 1=rampdown, 2=square, 3=random */
+ case 0: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break;
+ case 1: p_sample->set_vibrato_type( CPSample::VIBRATO_SAW ); break;
+ case 2: p_sample->set_vibrato_type( CPSample::VIBRATO_SQUARE ); break;
+ case 3: p_sample->set_vibrato_type( CPSample::VIBRATO_RANDOM ); break;
+ default: p_sample->set_vibrato_type( CPSample::VIBRATO_SINE ); break;
+ }
+
+ //printf("Name %s - Flags: fileofs :%i - c5spd %i - len %i 16b %i - data?: %i\n",p_sample->get_name(),aux_sample_data.fileofs,aux_sample_data.c5spd, aux_sample_data.length, aux_sample_data.is16bit,aux_sample_data.exists);
+ CPSample_ID samp_id;
+
+ if (aux_sample_data.exists) {
+ samp_id=load_sample_data(aux_sample_data);
+ CPSampleManager::get_singleton()->set_c5_freq(samp_id,aux_sample_data.c5spd);
+ CPSampleManager::get_singleton()->set_loop_begin( samp_id,aux_sample_data.loop_begin );
+ CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end );
+ CPSample_Loop_Type loop_type=aux_sample_data.loop_enabled?( aux_sample_data.pingpong_enabled? CP_LOOP_BIDI: CP_LOOP_FORWARD):CP_LOOP_NONE;
+ CPSampleManager::get_singleton()->set_loop_end( samp_id,aux_sample_data.loop_end );
+ CPSampleManager::get_singleton()->set_loop_type( samp_id, loop_type);
+
+ }
+
+ //printf("Loaded id is null?: %i\n",samp_id.is_null());
+ p_sample->set_sample_data(samp_id);
+ if (!samp_id.is_null()) {
+
+ // printf("Loaded ID: stereo: %i len %i 16bit %i\n",CPSampleManager::get_singleton()->is_stereo(samp_id), CPSampleManager::get_singleton()->get_size( samp_id), CPSampleManager::get_singleton()->is_16bits( samp_id) );
+ }
+
+ CP_ERR_COND_V( file->eof_reached(),FILE_CORRUPTED );
+ CP_ERR_COND_V( file->get_error(),FILE_CORRUPTED );
+
+ return FILE_OK;
+
+}
+
+CPSample_ID CPLoader_IT::load_sample_data(AuxSampleData& p_sample_data) {
+
+
+ int aux_sample_properties = (p_sample_data.is16bit?IT_SAMPLE_16BITS:0)|(p_sample_data.compressed?IT_SAMPLE_COMPRESSED:0)|(p_sample_data.stereo?IT_SAMPLE_STEREO:0);
+
+ file->seek(p_sample_data.fileofs);
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ CPSample_ID id;
+
+ switch (aux_sample_properties) {
+
+ case (0): // 8 bits, mono
+ case (IT_SAMPLE_16BITS): // 16 bits mono
+ case (IT_SAMPLE_STEREO): // 8 bits stereo
+ case (IT_SAMPLE_16BITS|IT_SAMPLE_STEREO): { // 16 bits mono
+
+ id=sm->create(p_sample_data.is16bit,p_sample_data.stereo,p_sample_data.length);
+ if (id.is_null())
+ break;
+
+ sm->lock_data(id);
+
+ int16_t *ptr16 = (int16_t*)sm->get_data(id);
+ int8_t *ptr8=(int8_t*)ptr16;
+
+ int chans=p_sample_data.stereo?2:1;
+
+ if (p_sample_data.is16bit) {
+
+ for (int c=0;c<chans;c++) {
+
+ for (int i=0;i<p_sample_data.length;i++) {
+
+ ptr16[i*chans+c]=file->get_word();
+ }
+ }
+ } else {
+
+ for (int c=0;c<chans;c++) {
+
+ for (int i=0;i<p_sample_data.length;i++) {
+
+ ptr8[i*chans+c]=file->get_byte();
+ }
+ }
+
+ }
+
+ sm->unlock_data(id);
+
+ } break;
+ case (IT_SAMPLE_COMPRESSED): { // 8 bits compressed
+
+
+ id=sm->create(false,false,p_sample_data.length);
+ if (id.is_null())
+ break;
+ sm->lock_data(id);
+
+ if ( load_sample_8bits_IT_compressed((void*)sm->get_data( id),p_sample_data.length) ) {
+
+ sm->unlock_data(id);
+ sm->destroy(id);
+
+ break;
+ }
+
+ sm->unlock_data(id);
+
+
+ } break;
+ case (IT_SAMPLE_16BITS|IT_SAMPLE_COMPRESSED): { // 16 bits compressed
+
+
+ id=sm->create(true,false,p_sample_data.length);
+ if (id.is_null())
+ break;
+ sm->lock_data(id);
+
+ if ( load_sample_16bits_IT_compressed((void*)sm->get_data(id),p_sample_data.length) ) {
+
+ sm->unlock_data(id);
+ sm->destroy(id);
+ break;
+ }
+
+ sm->unlock_data(id);
+
+ } break;
+ default: {
+
+ // I dont know how to handle stereo compressed, does that exist?
+ } break;
+
+ }
+
+
+ return id;
+}
+
+
+CPLoader::Error CPLoader_IT::load_samples() {
+
+ for (int i=0;i<header.smpnum;i++) {
+
+ //seek to sample
+ file->seek(0xC0+header.ordnum+header.insnum*4+i*4);
+
+ uint32_t final_location=file->get_dword();
+ file->seek( final_location );
+
+
+ Error err=load_sample(song->get_sample(i));
+ CP_ERR_COND_V(err,err);
+
+ }
+
+ if (file->eof_reached() || file->get_error())
+ return FILE_CORRUPTED;
+
+ return FILE_OK;
+}
+/* * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE
+
+ -The following sample decompression code is based on xmp's code.(http://xmp.helllabs.org) which is based in openCP code.
+
+* NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE */
+
+uint32_t CPLoader_IT::read_n_bits_from_IT_compressed_block (uint8_t p_bits_to_read) {
+
+ uint32_t aux_return_value;
+ uint32_t val;
+
+ uint8_t *buffer=(uint8_t*)source_position;
+ if ( p_bits_to_read <= source_remaining_bits ) {
+
+ val=buffer[3];
+ val<<=8;
+ val|=buffer[2];
+ val<<=8;
+ val|=buffer[1];
+ val<<=8;
+ val|=buffer[0];
+
+ aux_return_value = val & ((1 << p_bits_to_read) - 1);
+ val >>= p_bits_to_read;
+ source_remaining_bits -= p_bits_to_read;
+
+ buffer[3]=val>>24;
+ buffer[2]=(val>>16)&0xFF;
+ buffer[1]=(val>>8)&0xFF;
+ buffer[0]=(val)&0xFF;
+
+ } else {
+ aux_return_value=buffer[3];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[2];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[1];
+ aux_return_value<<=8;
+ aux_return_value|=buffer[0];
+
+ uint32_t nbits = p_bits_to_read - source_remaining_bits;
+ source_position++;
+
+ buffer+=4;
+ val=buffer[3];
+ val<<=8;
+ val|=buffer[2];
+ val<<=8;
+ val|=buffer[1];
+ val<<=8;
+ val|=buffer[0];
+ aux_return_value |= ((val & ((1 << nbits) - 1)) << source_remaining_bits);
+ val >>= nbits;
+ source_remaining_bits = 32 - nbits;
+ buffer[3]=val>>24;
+ buffer[2]=(val>>16)&0xFF;
+ buffer[1]=(val>>8)&0xFF;
+ buffer[0]=(val)&0xFF;
+
+ }
+
+ return aux_return_value;
+}
+
+bool CPLoader_IT::read_IT_compressed_block (bool p_16bits) {
+
+ uint16_t size;
+
+ size=file->get_word();
+
+ if (file->eof_reached() || file->get_error()) return true;
+
+ pat_data = (uint8_t*)CP_ALLOC( 4* ((size >> 2) + 2) );
+ if (!pat_data)
+ return true;
+
+
+ source_buffer=(uint32_t*)pat_data;
+ file->get_byte_array((uint8_t*)source_buffer,size);
+
+ if (file->eof_reached() || file->get_error()) {
+
+ free_IT_compressed_block();
+ return true;
+ }
+
+ source_position = source_buffer;
+ source_remaining_bits = 32;
+
+ return false;
+}
+
+void CPLoader_IT::free_IT_compressed_block () {
+
+
+ if (pat_data) {
+ CP_FREE(pat_data);
+ pat_data=NULL;
+ }
+
+}
+
+bool CPLoader_IT::load_sample_8bits_IT_compressed(void *p_dest_buffer,int p_buffsize) {
+
+ int8_t *dest_buffer; /* destination buffer which will be returned */
+ uint16_t block_length; /* length of compressed data block in samples */
+ uint16_t block_position; /* position in block */
+ uint8_t bit_width; /* actual "bit width" */
+ uint16_t aux_value; /* value read from file to be processed */
+ int8_t d1, d2; /* integrator buffers (d2 for it2.15) */
+ int8_t *dest_position; /* position in output buffer */
+ int8_t v; /* sample value */
+ bool it215; // is this an it215 module?
+
+ dest_buffer = (int8_t *) p_dest_buffer;
+
+ if (dest_buffer==NULL)
+ return true;
+
+ for (int i=0;i<p_buffsize;i++)
+ dest_buffer[i]=0;
+
+
+ dest_position = dest_buffer;
+
+ it215=(header.cmwt==0x215);
+
+ /* now unpack data till the dest buffer is full */
+
+ while (p_buffsize) {
+ /* read a new block of compressed data and reset variables */
+ if ( read_IT_compressed_block(false) ) {
+ CP_PRINTERR("Out of memory decompressing IT CPSample");
+ return true;
+ }
+
+
+ block_length = (p_buffsize < 0x8000) ? p_buffsize : 0x8000;
+
+ block_position = 0;
+
+ bit_width = 9; /* start with width of 9 bits */
+
+ d1 = d2 = 0; /* reset integrator buffers */
+
+ /* now uncompress the data block */
+ while ( block_position < block_length ) {
+
+ aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */
+
+ if ( bit_width < 7 ) { /* method 1 (1-6 bits) */
+
+ if ( aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */
+
+ aux_value = read_n_bits_from_IT_compressed_block(3) + 1; /* yes -> read new width; */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+ } else if ( bit_width < 9 ) { /* method 2 (7-8 bits) */
+
+ uint8_t border = (0xFF >> (9 - bit_width)) - 4;
+ /* lower border for width chg */
+
+ if ( aux_value > border && aux_value <= (border + 8) ) {
+
+ aux_value -= border; /* convert width to 1-8 */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+
+ } else if ( bit_width == 9 ) { /* method 3 (9 bits) */
+
+ if ( aux_value & 0x100 ) { /* bit 8 set? */
+
+ bit_width = (aux_value + 1) & 0xff; /* new width... */
+ continue; /* ... and next value */
+ }
+
+ } else { /* illegal width, abort */
+
+
+ free_IT_compressed_block();
+ CP_PRINTERR("CPSample has illegal BitWidth ");
+ return true;
+ }
+
+ /* now expand value to signed byte */
+ if ( bit_width < 8 ) {
+
+ uint8_t tmp_shift = 8 - bit_width;
+
+ v=(aux_value << tmp_shift);
+ v>>=tmp_shift;
+
+ } else v = (int8_t) aux_value;
+
+ /* integrate upon the sample values */
+ d1 += v;
+ d2 += d1;
+
+ /* ... and store it into the buffer */
+ *(dest_position++) = it215 ? d2 : d1;
+ block_position++;
+
+ }
+
+ /* now subtract block lenght from total length and go on */
+ free_IT_compressed_block();
+ p_buffsize -= block_length;
+ }
+
+
+ return false;
+}
+
+bool CPLoader_IT::load_sample_16bits_IT_compressed(void *p_dest_buffer,int p_buffsize) {
+
+ int16_t *dest_buffer; /* destination buffer which will be returned */
+ uint16_t block_length; /* length of compressed data block in samples */
+ uint16_t block_position; /* position in block */
+ uint8_t bit_width; /* actual "bit width" */
+ uint32_t aux_value; /* value read from file to be processed */
+ int16_t d1, d2; /* integrator buffers (d2 for it2.15) */
+ int16_t *dest_position; /* position in output buffer */
+ int16_t v; /* sample value */
+
+ bool it215; // is this an it215 module?
+
+ dest_buffer = (int16_t *) p_dest_buffer;
+
+ if (dest_buffer==NULL)
+ return true;
+
+ for (int i=0;i<p_buffsize;i++)
+ dest_buffer[i]=0;
+
+ dest_position = dest_buffer;
+
+ it215=(header.cmwt==0x215);
+
+
+ while (p_buffsize) {
+ /* read a new block of compressed data and reset variables */
+ if ( read_IT_compressed_block(true) ) {
+
+ return true;
+ }
+
+
+ block_length = (p_buffsize < 0x4000) ? p_buffsize : 0x4000;
+
+ block_position = 0;
+
+ bit_width = 17; /* start with width of 9 bits */
+
+ d1 = d2 = 0; /* reset integrator buffers */
+
+ while ( block_position < block_length ) {
+
+ aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */
+
+ if ( bit_width < 7 ) { /* method 1 (1-6 bits) */
+
+ if ( (signed)aux_value == (1 << (bit_width - 1)) ) { /* check for "100..." */
+
+ aux_value = read_n_bits_from_IT_compressed_block(4) + 1; /* yes -> read new width; */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+ } else if ( bit_width < 17 ) {
+
+ uint16_t border = (0xFFFF >> (17 - bit_width)) - 8;
+
+ if ( (int)aux_value > (int)border && (int)aux_value <= ((int)border + 16) ) {
+
+ aux_value -= border; /* convert width to 1-8 */
+ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1;
+ /* and expand it */
+ continue; /* ... next value */
+ }
+
+
+ } else if ( bit_width == 17 ) {
+
+ if ( aux_value & 0x10000 ) { /* bit 8 set? */
+
+ bit_width = (aux_value + 1) & 0xff; /* new width... */
+ continue; /* ... and next value */
+ }
+
+ } else { /* illegal width, abort */
+
+ CP_PRINTERR("CPSample has illegal BitWidth ");
+
+ free_IT_compressed_block();
+
+ return true;
+ }
+
+ /* now expand value to signed byte */
+ if ( bit_width < 16 ) {
+
+ uint8_t tmp_shift = 16 - bit_width;
+
+ v=(aux_value << tmp_shift);
+ v>>=tmp_shift;
+
+ } else v = (int16_t) aux_value;
+
+ /* integrate upon the sample values */
+ d1 += v;
+ d2 += d1;
+
+ /* ... and store it into the buffer */
+ *(dest_position++) = it215 ? d2 : d1;
+ block_position++;
+
+ }
+
+ /* now subtract block lenght from total length and go on */
+ free_IT_compressed_block();
+ p_buffsize -= block_length;
+ }
+
+
+ return false;
+
+}
+
+
+
diff --git a/modules/chibi/cp_loader_mod.cpp b/modules/chibi/cp_loader_mod.cpp
new file mode 100644
index 0000000000..f867b77914
--- /dev/null
+++ b/modules/chibi/cp_loader_mod.cpp
@@ -0,0 +1,482 @@
+/*************************************************************************/
+/* cp_loader_mod.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_loader_mod.h"
+
+
+static bool tag_equal_to(const char *p_tag, const char *p_string) {
+
+ return( p_tag[0]==p_string[0] &&
+ p_tag[1]==p_string[1] &&
+ p_tag[2]==p_string[2] &&
+ p_tag[3]==p_string[3]);
+}
+/* ProTracker period table */
+uint16_t period_table[6*12] = {
+ 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907,
+ 856,808,762,720,678,640,604,570,538,508,480,453,
+ 428,404,381,360,339,320,302,285,269,254,240,226,
+ 214,202,190,180,170,160,151,143,135,127,120,113,
+ 107,101,95,90,85,80,75,71,67,63,60,56,
+ 53,50,47,45,42,40,37,35,33,31,30,28
+};
+
+
+CPLoader::Error CPLoader_MOD::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+ //printf("Can't open file! %s\n",p_file);
+ return FILE_CANNOT_OPEN;
+ };
+
+ /* FIRST OF ALL, one needs to read the .mod file format tag */
+ file->seek( 1080 ); //located at 1080
+
+ char format_tag[4];
+
+ file->get_byte_array( (uint8_t*)format_tag, 4 );
+
+ int channels=-1;
+
+ /** THE PAIN!! - COMPARE TAGS */
+
+ /* Classic 4-chan */
+ if (tag_equal_to(format_tag,"M.K.") )
+ channels=4;
+ if (tag_equal_to(format_tag,"FLT4") )
+ channels=4;
+ if (tag_equal_to(format_tag,"M!K!") )
+ channels=4;
+
+ /* 8 Channel MODS */
+
+ if (tag_equal_to(format_tag,"FLT8") )
+ channels=2;
+
+ if (tag_equal_to(format_tag,"CD81") )
+ channels=2;
+
+ /* Custom channel MODS */
+
+ for (int i=1;i<=32;i++) {
+
+ if (i<10) { // up to 9 channels mods
+
+ /* Old Take Tracker */
+ char old_take_tracker[4]={'T','D','Z',char('0'+i)};
+
+ if (tag_equal_to(format_tag,old_take_tracker)) {
+
+ channels=i;
+ break;
+ }
+
+ /* Contemplates many XCHN Formats */
+ char xchn[4]={char('0'+i),'C','H','N'};
+
+ if (tag_equal_to(format_tag,xchn)) {
+
+ channels=i;
+ break;
+ }
+ }
+
+ /* Fast Tracker */
+ char fast_tracker[4]={char('0'+(i/10)),char('0'+(i%10)),'C','H'};
+
+ if (tag_equal_to(format_tag,fast_tracker)) {
+
+ channels=i;
+ break;
+ }
+
+ }
+
+
+ if (channels==-1) {
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+ }
+
+
+
+ /** Load CPSong INFO */
+
+ file->seek( 0 ); //go to begining of file
+
+ file->set_endian_conversion( true );
+ p_song->reset();
+ p_song->set_instruments( false );
+
+ char name[21];
+
+ file->get_byte_array( (uint8_t*)name,20);
+ name[20]=0;
+
+ p_song->set_name(name);
+ p_song->set_old_effects( true );
+ p_song->set_linear_slides( false );
+ p_song->set_compatible_gxx( true );
+
+
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ int instruments=31;
+
+ for (int i=0;i<instruments;i++) {
+
+ char sample_name[23];
+ file->get_byte_array( (uint8_t*)sample_name,22);
+ sample_name[22]=0;
+
+ uint32_t sample_len=file->get_word();
+ sample_len<<=1;
+
+ uint8_t fine_nibble=file->get_byte()&0xF;
+
+
+ //(int8_t)(fine_nibble & 7) - (int8_t)(fine_nibble & 8); //yesso's genius trick
+ // boo, I can't use it :( but i leave it here because of how cool it is
+ uint8_t linear_volume=file->get_byte(); //0 .. ?
+
+ uint32_t loop_begin=file->get_word(); //0 .. ?
+ loop_begin<<=1;
+ uint32_t loop_end=file->get_word(); //0 .. ?
+ loop_end<<=1;
+
+ if (sample_len>0) {
+
+ CPSample_ID sid=sm->create( false, false, sample_len );
+
+ if (sid.is_null()) {
+
+ file->close();
+ return FILE_OUT_OF_MEMORY;
+ }
+
+ if (loop_end>2) {
+ sm->set_loop_begin( sid, loop_begin );
+ sm->set_loop_end( sid, loop_end+loop_begin );
+ sm->set_loop_type( sid,CP_LOOP_FORWARD );
+ }
+ static const uint16_t fine_to_freq[16]={
+ 8363,8413,8463,8529,8581,8651,8723,8757,
+ 7895,7941,7985,8046,8107,8169,8232,8280
+ };
+
+ sm->set_c5_freq( sid, fine_to_freq[fine_nibble] );
+ p_song->get_sample(i)->set_sample_data(sid);
+ }
+
+ p_song->get_sample(i)->set_name(sample_name);
+ p_song->get_sample(i)->set_default_volume( linear_volume );
+
+
+
+ }
+
+ /* pan for MODs */
+ for (int i=0;i<channels;i++)
+ p_song->set_channel_pan( i, (((i&3)==1) || ((i&3)==2)) ? 0: 64);
+
+
+ uint8_t order_count=file->get_byte();
+// uint8_t loop_to=file->get_byte();
+
+
+ int pattern_count=0;
+
+ for (int i=0;i<128;i++) {
+
+ uint8_t order=file->get_byte();
+
+
+ if (i<order_count) {
+ p_song->set_order(i,order);
+
+ /* Determine the amount of patterns */
+ if ((order+1)>pattern_count)
+ pattern_count=order+1;
+ } else
+ p_song->set_order( i, CP_ORDER_NONE );
+ }
+
+ if (instruments==31)
+ file->get_dword(); // identiefier, now skip it
+
+ for (int i=0;i<pattern_count;i++) {
+
+ for(int line=0;line<64;line++) {
+
+ for(int column=0;column<channels;column++) {
+
+ uint32_t note_w=file->get_dword();
+
+ CPNote note;
+
+ note.instrument=(note_w>>12)&0xF;
+ note.instrument|=(note_w>>24)&0xF0;
+
+ if (note.instrument==0)
+ note.instrument=CPNote::EMPTY;
+ else
+ note.instrument--;
+
+ note.parameter=note_w&0xFF;
+
+ int cmd=(note_w>>8)&0xF;
+
+ uint32_t period=(note_w>>16)&0xFFF;
+
+ if (period>0 && period<0xFFF) {
+
+ //period>>=2;
+ //period<<=1;
+ for (int n=0; n<6*12; n++) {
+
+ if (period >= period_table[n]) {
+
+ if ((period!=period_table[n]) && (n))
+ {
+ uint32_t p1 = period_table[n-1];
+ uint32_t p2 = period_table[n];
+ if (p1 - period < (period - p2)) {
+
+ note.note=n+36;
+ break;
+ }
+ }
+ note.note=n+1+36;
+ break;
+ }
+ }
+ if (note.note==CPNote::EMPTY)
+ note.note=6*12+36;
+
+ note.note--;
+ }
+
+
+ switch(cmd) {
+
+ case 0x0: {
+
+ if (note.parameter>0)
+ note.command='J'-'A';
+ } break;
+ case 0x1: {
+ note.command='F'-'A';
+ } break;
+ case 0x2: {
+
+ note.command='E'-'A';
+ } break;
+ case 0x3: {
+
+ note.command='G'-'A';
+ } break;
+ case 0x4: {
+
+ note.command='H'-'A';
+ } break;
+ case 0x5: {
+ note.command='L'-'A';
+ } break;
+ case 0x6: {
+
+ note.command='K'-'A';
+ } break;
+ case 0x7: {
+ note.command='R'-'A';
+ } break;
+ case 0x8: {
+
+ note.command='X'-'A';
+ } break;
+ case 0x9: {
+
+ note.command='O'-'A';
+
+ } break;
+ case 0xA: {
+
+ note.command='D'-'A';
+
+ } break;
+ case 0xB: {
+
+ note.command='B'-'A';
+
+ } break;
+ case 0xC: {
+
+ note.volume=note.parameter;
+ if (note.volume>64)
+ note.volume=64;
+ note.parameter=0;
+
+ } break;
+ case 0xD: {
+
+ note.command='C'-'A';
+ note.parameter=(note.parameter>>4)*10 + (note.parameter&0xF);
+
+ } break;
+ case 0xE: { //SPECIAL EFFECT!
+
+ note.command='S'-'A';
+
+ switch(note.parameter>>4) {
+
+ case 0x1: {
+
+ note.command='F'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+ } break;
+ case 0x2: {
+
+ note.command='E'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+ } break;
+ case 0x4: {
+
+ note.command='S'-'A';
+ note.parameter=0x30|(note.parameter&0x3);
+
+ } break;
+ case 0x6: {
+
+ note.command='S'-'A';
+ note.parameter=0xB0|(note.parameter&0xF);
+
+ } break;
+ case 0x7: {
+ note.command='S'-'A';
+ note.parameter=0x40|(note.parameter&0x3);
+
+ } break;
+ case 0x8: {
+
+ note.command='S'-'A'; // wow, it's the same!
+
+ } break;
+ case 0x9: {
+ note.command='Q'-'A';
+ note.parameter=(note.parameter&0xF);
+
+ } break;
+ case 0xA: {
+
+ note.command='D'-'A';
+ note.parameter=0xF|((note.parameter&0xF)<<4);
+
+ } break;
+ case 0xB: {
+ note.command='D'-'A';
+ note.parameter=0xF0|(note.parameter&0xF);
+
+ } break;
+ case 0xC:
+ case 0xD: {
+
+ note.command='S'-'A'; //wow, they are the same!
+
+ } break;
+ case 0xE: {
+ note.command='S'-'A';
+ note.parameter=0x60|(note.parameter&0xF);
+
+ } break;
+
+ default: {
+
+ note.command=CPNote::EMPTY;
+ note.parameter=0;
+ } break;
+
+ }
+ } break;
+ case 0xF: {
+
+ if (note.parameter<32)
+ note.command='A'-'A';
+ else
+ note.command='T'-'A';
+
+ } break;
+ }
+
+ p_song->get_pattern(i)->set_note( column,line, note );
+ }
+ }
+ }
+
+
+
+ for (int i=0;i<instruments;i++) {
+
+ CPSample_ID sid=p_song->get_sample(i)->get_sample_data();
+ if (sid.is_null()) {
+ continue; //empty sample, not stored?
+ }
+ sm->lock_data(sid);
+ uint8_t *dataptr = (uint8_t*)sm->get_data(sid);
+
+ int len=sm->get_size(sid);
+ for (int s=0;s<len;s++) {
+
+ uint8_t d=file->get_byte();
+ //d-=128; //convert to signed
+ int8_t*ds=(int8_t*)&d;
+ dataptr[s]=*ds;
+
+ }
+ sm->unlock_data(sid);
+ }
+
+ file->close();
+
+ return FILE_OK;
+
+
+}
+
+
+CPLoader_MOD::CPLoader_MOD(CPFileAccessWrapper *p_file) {
+
+ file=p_file;
+}
+
+
+CPLoader_MOD::~CPLoader_MOD()
+{
+}
+
+
diff --git a/modules/chibi/cp_loader_mod.h b/modules/chibi/cp_loader_mod.h
new file mode 100644
index 0000000000..636f4f00f2
--- /dev/null
+++ b/modules/chibi/cp_loader_mod.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* cp_loader_mod.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_LOADER_MOD_H
+#define CP_LOADER_MOD_H
+#include "cp_loader.h"
+/**
+ @author Juan Linietsky <reduz@gmail.com>
+*/
+class CPLoader_MOD : public CPLoader {
+
+ CPFileAccessWrapper *file;
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return false; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample) { return FILE_UNRECOGNIZED; }
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) { return FILE_UNRECOGNIZED; }
+
+ CPLoader_MOD(CPFileAccessWrapper *p_file);
+ ~CPLoader_MOD();
+};
+
+#endif
diff --git a/modules/chibi/cp_loader_s3m.cpp b/modules/chibi/cp_loader_s3m.cpp
new file mode 100644
index 0000000000..0fc15c1e2f
--- /dev/null
+++ b/modules/chibi/cp_loader_s3m.cpp
@@ -0,0 +1,413 @@
+/*************************************************************************/
+/* cp_loader_s3m.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_s3m.h"
+
+#define BITBOOL(m_exp) ((m_exp)?1:0)
+
+
+CPLoader::Error CPLoader_S3M::load_header() {
+
+ int i;
+
+
+ file->get_byte_array((uint8_t*)header.songname,28);
+ header.t1a=file->get_byte();
+ header.type=file->get_byte();
+ file->get_byte_array((uint8_t*)header.unused1,2);
+ header.ordnum=file->get_word();
+ header.insnum=file->get_word();
+ header.patnum=file->get_word();
+ header.flags=file->get_word();
+ header.tracker=file->get_word();
+ header.fileformat=file->get_word();
+ file->get_byte_array((uint8_t*)header.scrm,4);
+ header.scrm[4]=0;
+
+ if (header.scrm[0]!='S' || header.scrm[1]!='C' || header.scrm[2]!='R' || header.scrm[3]!='M')
+ return FILE_UNRECOGNIZED;
+
+ header.mastervol=file->get_byte();
+ header.initspeed=file->get_byte();
+ header.inittempo=file->get_byte();
+ header.mastermult=file->get_byte();
+ header.ultraclick=file->get_byte();
+ header.pantable=file->get_byte();
+ file->get_byte_array((uint8_t*)header.unused2,8);
+ header.special=file->get_word();
+ file->get_byte_array((uint8_t*)header.channels,32);
+
+ file->get_byte_array((uint8_t*)header.orderlist,header.ordnum);
+
+ header.scrm[4]=0;
+ if (header.scrm[0]!='S' || header.scrm[1]!='C' || header.scrm[2]!='R' || header.scrm[3]!='M') //again?
+ return FILE_UNRECOGNIZED;
+ //sample parapointers
+ for (i=0;i<header.insnum;i++) {
+
+ int parapointer;
+ parapointer=file->get_word();
+ parapointer=(parapointer*16);
+ sample_parapointers[i]=parapointer;
+ }
+ //pattern
+ for (i=0;i<header.patnum;i++) {
+
+ int parapointer;
+ parapointer=file->get_word();
+ parapointer=(parapointer*16);
+ pattern_parapointers[i]=parapointer;
+ }
+
+ if (header.pantable==252) {
+
+ file->get_byte_array((uint8_t*)header.pannings,32);
+ }
+
+ return FILE_OK;
+
+
+}
+
+
+void CPLoader_S3M::set_header() {
+
+
+
+
+ song->set_name( header.songname );
+// song->variables.filename=
+
+ song->set_row_highlight_minor( 4 );
+ song->set_row_highlight_major( 16 );
+ song->set_mixing_volume( header.mastervol );
+ song->set_linear_slides( false );
+ song->set_old_effects( !(header.flags&64) );
+ song->set_compatible_gxx( true );
+
+ song->set_global_volume( header.mastermult );
+ song->set_speed( header.initspeed );
+ song->set_tempo( header.inittempo );
+
+ //[TODO] Set Panning Positions.. ?
+
+ for (int i=0;i<header.ordnum;i++) song->set_order(i,header.orderlist[i]);
+
+}
+
+CPLoader::Error CPLoader_S3M::load_sample(CPSample *p_sample) {
+
+
+
+ int type=file->get_byte();
+
+ char filename[13];
+ file->get_byte_array((uint8_t*)filename,12);
+ filename[12]=0;
+
+
+ uint32_t samplepos=(uint32_t)file->get_byte() << 16;
+ samplepos|=file->get_word();
+ samplepos*=16;
+// printf("sample at %i\n",samplepos);
+ /**/
+ int sample_size=file->get_dword();
+
+
+ int loop_begin=file->get_dword();
+ int loop_end=file->get_dword();
+
+ int def_volume=file->get_byte();;
+ int dsk=file->get_byte();
+ int pack=file->get_byte();
+
+ int flags=file->get_byte();
+ int c2speed=file->get_dword();
+
+ file->get_dword(); //useless crap
+ file->get_dword();
+ file->get_dword();
+
+
+ char name[29];
+ file->get_byte_array((uint8_t*)name,28);
+ name[28]=0;
+
+ p_sample->set_default_volume(def_volume);
+ p_sample->set_name(name);
+
+ char scrs[5];
+ file->get_byte_array((uint8_t*)scrs,4);
+ scrs[4]=0;
+
+
+
+ bool data_is_16bits=flags&4;
+ bool data_is_stereo=flags&2;
+
+ if (type==0) {
+ //empty sample
+ return FILE_OK;
+ }
+
+
+ if ((type!=1) || scrs[0]!='S' || scrs[1]!='C' || scrs[2]!='R' || scrs[3]!='S' ) {
+// printf("type: %i, %c%c%c%c\n",type,scrs[0],scrs[1],scrs[2],scrs[3]);
+ CP_PRINTERR("Not an S3M CPSample!");
+ return FILE_CORRUPTED;
+ }
+
+ //p_sample->data.set_c5_freq(p_sample->c2spd<<1);
+
+ file->seek(samplepos);
+
+ int real_sample_size=sample_size<<BITBOOL(data_is_16bits);
+ real_sample_size<<=BITBOOL(data_is_stereo);
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ CPSample_ID id =sm->create( data_is_16bits, data_is_stereo, sample_size );
+
+ if (id.is_null())
+ return FILE_OUT_OF_MEMORY;
+
+ sm->lock_data(id);
+ void *dataptr = sm->get_data(id);
+
+ int chans = (data_is_stereo?2:1);
+ for (int c=0;c<chans;c++) {
+ for (int i=0;i<sample_size;i++) {
+
+ if (data_is_16bits) {
+
+ uint16_t s=file->get_word();
+ s-=32768; //toggle sign
+
+ int16_t *v=(int16_t*)&s;
+ ((int16_t*)dataptr)[i*chans+c]=*v;
+ } else {
+
+
+ int8_t *v;
+ uint8_t s=file->get_byte();
+ s-=128; //toggle sign
+ v=(int8_t*)&s;
+ ((int8_t*)dataptr)[i*chans+c]=*v;
+
+ }
+
+ }
+
+ }
+
+ sm->unlock_data(id);
+
+
+ sm->set_loop_begin( id, loop_begin );
+ sm->set_loop_end( id, loop_end );
+ sm->set_loop_type( id, (flags&1) ? CP_LOOP_FORWARD : CP_LOOP_NONE );
+ sm->set_c5_freq( id, c2speed << 1 );
+ p_sample->set_sample_data(id);
+
+ /* Scream tracker previous to 3.10 seems to be buggy, as in, wont save what is after the sample loop, including the loop end point. Because of this I must fix it by habd */
+ if (flags&1) {
+
+ for (int c=0;c<(data_is_stereo?2:1);c++) {
+ sm->set_data( id, loop_end, sm->get_data( id, loop_begin,c ),c );
+
+ }
+ }
+
+
+ return FILE_OK;
+
+}
+
+
+CPLoader::Error CPLoader_S3M::load_pattern(CPPattern *p_pattern) {
+
+ int row=0,flag,ch;
+ CPNote n;
+ int length,accum=0;
+
+ length=file->get_word();
+ p_pattern->set_length(64);
+
+ /* clear pattern data */
+ while((row<64) && (accum<=length) ) {
+ flag=file->get_byte();
+ accum++;
+
+ n.clear();
+ if(flag) {
+ // ch=remap[flag&31];
+// ch=remap[flag&31];
+// if(ch!=-1)
+// n=s3mbuf[(64U*ch)+row];
+// else
+// n=&dummy;
+
+ ch=flag&31;
+
+ if(flag&32) {
+ n.note=file->get_byte();
+ if (n.note==255) {
+
+ n.note=CPNote::EMPTY;
+ } else if (n.note==254) {
+
+ n.note=CPNote::CUT;
+ } else {
+
+ n.note=((n.note>>4)*12)+(n.note&0xF);
+ }
+
+ n.instrument=file->get_byte()-1;
+ accum+=2;
+
+ }
+ if(flag&64) {
+ n.volume=file->get_byte();
+ if (n.volume>64) n.volume=64;
+ accum++;
+
+ }
+ if(flag&128) {
+ n.command=file->get_byte()-1;
+ n.parameter=file->get_byte();
+ accum+=2;
+ }
+
+ p_pattern->set_note(ch,row,n);
+ } else row++;
+ }
+ return FILE_OK;
+
+
+}
+
+CPLoader::Error CPLoader_S3M::load_sample(const char *p_file,CPSample *p_sample) {
+
+ return FILE_UNRECOGNIZED;
+}
+CPLoader::Error CPLoader_S3M::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ return FILE_UNRECOGNIZED;
+
+}
+
+
+CPLoader::Error CPLoader_S3M::load_samples() {
+
+ int i;
+
+ for(i=0;i<header.insnum;i++) {
+
+ file->seek(sample_parapointers[i]);
+ load_sample(song->get_sample(i));
+ sample_count++;
+ }
+
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_S3M::load_patterns() {
+
+ int i;
+
+ Error err;
+ for(i=0;i<header.patnum;i++) {
+
+ file->seek(pattern_parapointers[i]);
+
+ err=load_pattern(song->get_pattern(i) );
+ CP_ERR_COND_V(err,err);
+
+
+ pattern_count++;
+ }
+ return FILE_OK;
+
+}
+
+CPLoader::Error CPLoader_S3M::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ song=p_song;
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+ //printf("Can't open file! %s\n",p_file);
+ return FILE_CANNOT_OPEN;
+ };
+
+ sample_count=0;
+ pattern_count=0;
+
+ //printf("LOADING HEADER\n");
+ CPLoader::Error err;
+ if ((err=load_header())) {
+ file->close();
+ CP_ERR_COND_V(err,err);
+
+ }
+
+ song->reset(); //file type recognized, reset song!
+
+ set_header();
+
+ //printf("LOADING SAMPLES\n");
+
+ if ((err=load_samples())) {
+ file->close();
+
+ CP_ERR_COND_V(err,err);
+ }
+
+ //printf("LOADING PATTERNS\n");
+
+ if ((err=load_patterns())) {
+
+ file->close();
+ return err;
+ }
+
+ file->close();
+
+ return FILE_OK;
+}
+
+
+
+CPLoader_S3M::CPLoader_S3M(CPFileAccessWrapper *p_file){
+
+ file=p_file;
+
+}
+CPLoader_S3M::~CPLoader_S3M(){
+}
+
diff --git a/modules/chibi/cp_loader_s3m.h b/modules/chibi/cp_loader_s3m.h
new file mode 100644
index 0000000000..175e5e80fe
--- /dev/null
+++ b/modules/chibi/cp_loader_s3m.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* cp_loader_s3m.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_S3M_H
+#define CP_LOADER_S3M_H
+
+#include "cp_loader.h"
+
+/**
+ *@author Juan Linietsky
+ */
+/******************************
+ loader_s3m.h
+ ----------
+Scream Tracker Module CPLoader!
+It lacks support for
+individual sample loading
+and reorganizing the columns.
+********************************/
+
+
+
+
+class CPLoader_S3M : public CPLoader {
+
+ struct S3M_Header {
+ char songname[28];
+ uint8_t t1a;
+ uint8_t type;
+ uint8_t unused1[2];
+ uint16_t ordnum;
+ uint16_t insnum;
+ uint16_t patnum;
+ uint16_t flags;
+ uint16_t tracker;
+ uint16_t fileformat;
+ char scrm[5];
+ uint8_t mastervol;
+ uint8_t initspeed;
+ uint8_t inittempo;
+ uint8_t mastermult;
+ uint8_t ultraclick;
+ uint8_t pantable;
+ uint8_t unused2[8];
+ uint16_t special;
+ uint8_t channels[32];
+ uint8_t pannings[32];
+ uint8_t orderlist[300];
+ };
+
+
+ int sample_parapointers[CPSong::MAX_SAMPLES];
+ int pattern_parapointers[CPSong::MAX_PATTERNS];
+
+ Error load_header();
+ void set_header();
+ Error load_sample(CPSample *p_sample);
+ Error load_pattern(CPPattern *p_pattern);
+ Error load_patterns();
+
+ Error load_samples();
+
+ S3M_Header header;
+ int sample_count;
+ int pattern_count;
+
+ CPFileAccessWrapper *file;
+ CPSong *song;
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return false; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+ CPLoader_S3M(CPFileAccessWrapper *p_file);
+ ~CPLoader_S3M();
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_loader_xm.cpp b/modules/chibi/cp_loader_xm.cpp
new file mode 100644
index 0000000000..bff8615a32
--- /dev/null
+++ b/modules/chibi/cp_loader_xm.cpp
@@ -0,0 +1,752 @@
+/*************************************************************************/
+/* cp_loader_xm.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_loader_xm.h"
+#include "cp_tables.h"
+
+#define ABORT_LOAD { file->close(); return FILE_CORRUPTED; }
+
+
+
+
+CPLoader::Error CPLoader_XM::load_song(const char *p_file,CPSong *p_song,bool p_sampleset) {
+
+ song=p_song;
+
+ if (file->open(p_file,CPFileAccessWrapper::READ)) {
+
+ return FILE_CANNOT_OPEN;
+ };
+
+
+ /**************************************
+ LOAD HEADER
+ ***************************************/
+
+ file->get_byte_array(header.idtext,17);
+ header.idtext[17]=0;
+
+ file->get_byte_array(header.songname,20);
+
+
+
+ header.songname[20]=0;
+ header.hex1a=file->get_byte();
+ if (header.hex1a!=0x1A) { //XM "magic" byte.. this sucks :)
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+
+ }
+
+
+ //magic byte sucks, but can't do much about it..
+
+ song->reset(); //must reset the song
+
+ song->set_name( (const char*)header.songname );
+
+ file->get_byte_array(header.trackername,20);
+ header.trackername[20]=0;
+
+
+ header.version=file->get_word();
+
+ header.headersize=file->get_dword();
+
+ header.songlength=file->get_word();
+
+ header.restart_pos=file->get_word();
+
+ header.channels_used=file->get_word();
+
+ header.patterns_used=file->get_word();
+
+ header.instruments_used=file->get_word();
+
+ song->set_linear_slides( file->get_word() );
+
+ song->set_speed( file->get_word() );
+
+ song->set_tempo( file->get_word() );
+ song->set_instruments( true );
+
+ file->get_byte_array(header.orderlist,256);
+
+ for (int i=0;i<header.songlength;i++) {
+
+ if (i>199)
+ break;
+ song->set_order(i,header.orderlist[i]);
+ }
+
+ /**************************************
+ LOAD PATTERNS
+ ***************************************/
+
+ for (int i=0;i<header.patterns_used;i++) {
+
+ uint32_t aux,rows;
+
+ aux=file->get_dword(); //length
+ aux=file->get_byte(); //packing type
+ rows=aux=file->get_word(); //rows!
+
+ song->get_pattern(i)->set_length( aux );
+
+ aux=file->get_word(); //packed size
+ if (aux==0)
+ continue;
+ //unpaaack!
+ for(int j=0;j<(int)rows;j++)
+ for(int k=0;k<header.channels_used;k++) {
+
+ CPNote aux_note;
+ uint8_t aux_byte;
+ //uint8_t field;
+ aux_byte=file->get_byte(); //packing type
+ if (!(aux_byte&0x80)) {
+
+ aux_note.note=aux_byte;
+ aux_byte=0xFE; //if bit 7 not set, read all of them except the note
+ }
+
+ if (aux_byte&1) aux_note.note=file->get_byte();
+ if (aux_byte&2) aux_note.instrument=file->get_byte();
+ if (aux_byte&4) aux_note.volume=file->get_byte();
+ if (aux_byte&8) aux_note.command=file->get_byte();
+ if (aux_byte&16) aux_note.parameter=file->get_byte();
+
+ if (aux_note.note!=CPNote::EMPTY) {
+
+ if (aux_note.note==97) aux_note.note=CPNote::OFF;
+ else {
+ aux_note.note+=11; //octave minus one (XM C-0 is 1, not zero )
+ }
+ }
+ if (aux_note.instrument!=CPNote::EMPTY) {
+
+ if ((aux_note.instrument>0) && (aux_note.instrument<100))
+ aux_note.instrument--;
+ else
+ aux_note.instrument=CPNote::EMPTY;
+ }
+ if (aux_note.volume!=CPNote::EMPTY) {
+
+ if (aux_note.volume<0x10) {}
+ else if (aux_note.volume<0x50) {
+
+ aux_note.volume-=0x10;
+
+ } else if (aux_note.volume<0x60) {
+ //
+ aux_note.volume=CPNote::EMPTY;
+
+ } else if (aux_note.volume<0x70) {
+ //60 -- volume slide down
+ aux_note.volume-=0x60;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=95;
+
+ } else if (aux_note.volume<0x80) {
+ //70 -- volume slide up
+ aux_note.volume-=0x70;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=85;
+
+
+ } else if (aux_note.volume<0x90) {
+ //80 -- fine volume slide down
+ aux_note.volume-=0x80;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=75;
+
+
+ } else if (aux_note.volume<0xA0) {
+ //9 -- fine volume slide up
+
+ aux_note.volume-=0x90;
+ if (aux_note.volume>9) aux_note.volume=9;
+
+ aux_note.volume+=65;
+
+
+
+ } else if (aux_note.volume<0xB0) {
+ //A -- set vibrato speed
+ aux_note.volume=CPNote::EMPTY;
+
+ } else if (aux_note.volume<0xC0) {
+ //B -- vibrato
+ aux_note.volume-=0xB0;
+ if (aux_note.volume>9) aux_note.volume=9;
+ aux_note.volume+=203;
+
+
+ } else if (aux_note.volume<0xD0) {
+ //C -- set panning
+ int aux=aux_note.volume-=0xC0;
+ aux=aux*65/0xF;
+ aux_note.volume=128+aux;
+
+ } else if (aux_note.volume<0xE0) {
+ aux_note.volume=CPNote::EMPTY;
+
+
+ } else if (aux_note.volume<0xF0) {
+ aux_note.volume=CPNote::EMPTY;
+
+
+ } else {
+ //F -- tone porta
+ aux_note.volume-=0xF0;
+ aux_note.volume*=9;
+ aux_note.volume/=0xF;
+ aux_note.volume+=193;
+ }
+ }
+ if (aux_note.command!=CPNote::EMPTY) {
+
+ switch(aux_note.command) {
+
+ case 0x0:
+ aux_note.command='J'-'A';
+ break;
+ case 0x1:
+ aux_note.command='F'-'A';
+ break;
+ case 0x2:
+ aux_note.command='E'-'A';
+ break;
+ case 0x3:
+ aux_note.command='G'-'A';
+ break;
+ case 0x4:
+ aux_note.command='H'-'A';
+ break;
+ case 0x5:
+ aux_note.command='L'-'A';
+ break;
+ case 0x6:
+ aux_note.command='K'-'A';
+ break;
+ case 0x7:
+ aux_note.command='R'-'A';
+ break;
+ case 0x8:
+ aux_note.command='X'-'A';
+ break;
+ case 0x9:
+ aux_note.command='O'-'A';
+ break;
+ case 0xa:
+ aux_note.command='D'-'A';
+ break;
+ case 0xb:
+ aux_note.command='B'-'A';
+ break;
+ case 0xc:
+ //printf("XM Import: Warning! effect C (set volume) not implemented!\n");
+ break;
+ case 0xd:
+ aux_note.command='C'-'A';
+ break;
+
+ case 0xe: /* Extended effects */
+
+ aux_note.command='S'-'A';
+ switch(aux_note.parameter>>4) {
+ case 0x1: /* XM fine porta up */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='F'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+ break;
+ case 0x2: /* XM fine porta down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='E'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+ break;
+ case 0xa: /* XM fine volume up */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='D'-'A';
+ aux_note.parameter=0x0F|((aux_note.parameter&0xF)<<4);
+
+ break;
+ case 0xb: /* XM fine volume down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='D'-'A';
+ aux_note.parameter=0xF0|(aux_note.parameter&0xF);
+
+ break;
+ case 0x9: /* XM fine volume down */
+ if (!(aux_note.parameter&0xF)) { aux_note.command=CPNote::EMPTY; aux_note.parameter=0; break; }
+ aux_note.command='Q'-'A';
+ aux_note.parameter=0x00|(aux_note.parameter&0xF);
+ break;
+
+ case 0xc: //notecut
+
+ aux_note.parameter=0xC0|(aux_note.parameter&0xF);
+ break;
+
+ case 0xd: //notedelay
+
+ aux_note.parameter=0xD0|(aux_note.parameter&0xF);
+ break;
+
+ case 0xe: //patterndelay
+
+ aux_note.parameter=0xE0|(aux_note.parameter&0xF);
+ break;
+ }
+
+ break;
+ case 0xf:
+ if (aux_note.parameter<32) {
+ aux_note.command='A'-'A';
+ } else {
+ aux_note.command='T'-'A';
+ }
+ break;
+ case 'G'-55:
+ aux_note.command='V'-'A';
+ break;
+ case 'H'-55:
+ aux_note.command='W'-'A';
+ break;
+ case 'K'-55:
+ if (aux_note.note!=CPNote::EMPTY) break;
+ aux_note.note=CPNote::OFF;
+ break;
+ case 'P'-55:
+ aux_note.command='P'-'A';
+ break;
+ case 'R'-55:
+ aux_note.command='Q'-'A';
+ break;
+ case 'T'-55:
+ aux_note.command='I'-'A';
+ break;
+ default: {
+
+ aux_note.command=CPNote::EMPTY;
+ }
+ }
+
+
+ }
+
+ song->get_pattern( i)->set_note( k,j,aux_note );
+ }
+ }
+
+ /**************************************
+ LOAD INSTRUMENTS!
+ ***************************************/
+
+ for (int i=0;i<header.instruments_used;i++) {
+
+
+ uint32_t aux;
+ int sampnum;
+
+ CPInstrument &instrument=*song->get_instrument(i);
+ uint32_t cpos=file->get_pos();
+ //printf("pos is %i\n",cpos);
+
+
+
+/* +4 */ uint32_t hsize=file->get_dword(); //header length
+
+ char instrname[23];
+ instrname[22]=0;
+
+ file->get_byte_array((uint8_t*)instrname,22);
+//XM_LOAD_DEBUG printf("name is %s\n",instrname);
+
+/* +27 */ aux=file->get_byte(); //byte that must be ignored
+//XM_LOAD_DEBUG printf("header size is %i\n",hsize);
+
+/* +29 */ sampnum=file->get_word();
+
+//XM_LOAD_DEBUG printf("samples %i\n",sampnum);
+
+
+ instrument.set_name( instrname );
+// printf("Header Len: %i, CPInstrument %i, %i samples , name: s,\n",hsize,i,sampnum,instrname);
+
+ if (sampnum==0) {
+ //aux=file->get_dword(); //Why is this for? -- for nothing, skipped
+ if (hsize) {
+
+ file->seek( cpos+hsize ); //skip header if size has been specified
+ }
+ continue;
+ }
+
+/* +33 */ file->get_dword();
+
+ if (Error result=load_instrument_internal(&instrument,false,cpos,hsize,sampnum)) {
+
+ CP_PRINTERR("Error loading instrument");
+ file->close();
+ return result;
+ }
+
+ }
+//
+ file->close();
+ return FILE_OK;
+}
+
+CPLoader::Error CPLoader_XM::load_instrument_internal(CPInstrument *p_instr,bool p_xi,int p_cpos, int p_hsize, int p_sampnum) {
+
+ int sampnum;
+ uint32_t aux;
+ uint8_t notenumb[96];
+ uint16_t panenv[24],volenv[24];
+ int volpoints,panpoints;
+ int vol_loop_begin,vol_loop_end,vol_sustain_loop;
+ int pan_loop_begin,pan_loop_end,pan_sustain_loop;
+ char instrname[23];
+ int sample_index[16]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; //-1 means no index!
+
+ instrname[22]=0;
+
+
+/* +129 */ file->get_byte_array((uint8_t*)notenumb,96);
+ for (int j=0;j<24;j++) {
+ volenv[j]=file->get_word();
+ }
+ for (int j=0;j<24;j++) {
+ panenv[j]=file->get_word();
+ }
+
+/* +177 */
+/* +225 */
+/* +226 */ volpoints=file->get_byte();
+/* +227 */ panpoints=file->get_byte();
+/* +230 */ vol_sustain_loop=file->get_byte();
+/* +228 */ vol_loop_begin=file->get_byte();
+/* +229 */ vol_loop_end=file->get_byte();
+
+//XM_LOAD_DEBUG printf("1- volpoints: %i, panpoints: %i, susloop: %i, loop begin: %i, loop end %i\n",volpoints,panpoints,vol_sustain_loop,vol_loop_begin,vol_loop_end);
+ pan_sustain_loop=file->get_byte();
+/* +231 */ pan_loop_begin=file->get_byte();
+/* +232 */ pan_loop_end=file->get_byte();
+
+
+
+/* +234 */ aux=file->get_byte();
+ p_instr->get_volume_envelope()->reset();
+ p_instr->get_volume_envelope()->set_enabled(aux&1);
+ p_instr->get_volume_envelope()->set_sustain_loop_enabled((aux&2)?true:false);
+ p_instr->get_volume_envelope()->set_loop_enabled((aux&4)?true:false);
+/* +235 */ aux=file->get_byte();
+ p_instr->get_pan_envelope()->reset();
+ p_instr->get_pan_envelope()->set_enabled(aux&1);
+ p_instr->get_pan_envelope()->set_sustain_loop_enabled((aux&2)?true:false);
+ p_instr->get_pan_envelope()->set_loop_enabled((aux&4)?true:false);
+
+/* +239 */ aux=file->get_dword(); // sadly, cant use those
+/* +241 */ p_instr->set_volume_fadeout( file->get_word() >> 4 );
+/* +243 */ aux=file->get_word(); // reserved!
+
+
+
+ for (int j=0;j<volpoints;j++) {
+ int ofs=volenv[j*2];
+ int val=volenv[j*2+1];
+ p_instr->get_volume_envelope()->add_position(ofs,val);
+
+ }
+
+ //make sure minimum is 2
+ while (p_instr->get_volume_envelope()->get_node_count()<2) {
+
+ p_instr->get_volume_envelope()->add_position( p_instr->get_volume_envelope()->get_node_count()*20,64 );
+ }
+
+ for (int j=0;j<panpoints;j++) {
+ int ofs=panenv[j*2];
+ int val=panenv[j*2+1];
+ p_instr->get_pan_envelope()->add_position(ofs,val-32);
+ }
+
+ //make sure minimum is 2
+ while (p_instr->get_pan_envelope()->get_node_count()<2) {
+
+ p_instr->get_pan_envelope()->add_position( p_instr->get_pan_envelope()->get_node_count()*20,0 );
+ }
+
+
+ p_instr->get_volume_envelope()->set_loop_begin(vol_loop_begin);
+ p_instr->get_volume_envelope()->set_loop_end(vol_loop_end);
+ p_instr->get_volume_envelope()->set_sustain_loop_end(vol_sustain_loop);
+ p_instr->get_volume_envelope()->set_sustain_loop_begin(vol_sustain_loop);
+ p_instr->get_pan_envelope()->set_loop_begin(pan_loop_begin);
+ p_instr->get_pan_envelope()->set_loop_end(pan_loop_end);
+ p_instr->get_pan_envelope()->set_sustain_loop_end(pan_sustain_loop);
+ p_instr->get_pan_envelope()->set_sustain_loop_begin(pan_sustain_loop);
+
+
+ if (!p_xi) {
+
+ if ((file->get_pos()-p_cpos)<p_hsize) {
+
+ uint8_t junkbuster[500];
+
+ //printf("extra junk XM instrument in header! hsize is %i, extra junk: %i\n",p_hsize,(file->get_pos()-p_cpos));
+ //printf("extra: %i\n",p_hsize-(file->get_pos()-p_cpos));
+ file->get_byte_array((uint8_t*)junkbuster,p_hsize-(file->get_pos()-p_cpos));
+ }
+
+ sampnum=p_sampnum;
+ } else {
+
+ uint8_t junkbuster[500];
+ file->get_byte_array((uint8_t*)junkbuster,20); //14 bytes?
+
+ sampnum=file->get_word();
+
+ }
+
+
+ CPSampleManager *sm=CPSampleManager::get_singleton();
+
+ /*SAMPLE!!*/
+
+ for (int j=0;j<sampnum;j++) {
+
+ if (j>16) ABORT_LOAD;
+
+
+ int s_idx=-1;
+ for (int s=0;s<CPSong::MAX_SAMPLES;s++) {
+
+ if (song->get_sample(s)->get_sample_data().is_null()) {
+ //empty sample!
+ s_idx=s;
+ break;
+ }
+ }
+
+ if (s_idx==-1) ABORT_LOAD;
+ //printf("free sample: %i\n",s_idx);
+
+
+ CPSample& sample=*song->get_sample(s_idx);
+
+ int sample_size=file->get_dword();
+ int tmp_loop_begin=file->get_dword();
+
+ int tmp_loop_end=file->get_dword();
+
+ sample.set_default_volume(file->get_byte());
+
+ uint8_t ftb=file->get_byte();
+ int8_t *fts=(int8_t*)&ftb;
+ int finetune=*fts;
+ uint32_t flags=file->get_byte();
+
+ if (flags&16) { // is 16 bits.. at flag 16.. fun :)
+
+ tmp_loop_end/=2;
+ tmp_loop_begin/=2;
+ sample_size/=2;
+ }
+
+
+ CPSample_ID sample_data=sm->create( flags&16, false, sample_size );
+
+ sample.set_sample_data(sample_data);
+ sm->set_loop_begin(sample_data,tmp_loop_begin);
+ sm->set_loop_end(sample_data,tmp_loop_end+tmp_loop_begin);
+
+ sm->set_loop_type( sample_data, (flags&3)?( (flags&2) ? CP_LOOP_BIDI : CP_LOOP_FORWARD ):CP_LOOP_NONE );
+
+
+
+ sample.set_pan_enabled(true);
+ sample.set_pan(file->get_byte()*64/255);
+ uint8_t noteb=file->get_byte();
+ int8_t *notes=(int8_t*)&noteb;
+ int note_offset=*notes;
+ note_offset+=48;
+ //note_offset+=60;
+
+
+
+ //int linear_period=10*12*16*4 - (note_offset)*16*4 - finetune/2;
+ //int freq=(int)(8363*pow(2.0,(double)(6*12*16*4 - linear_period) / (double)(12*16*4)));
+
+ //sm->set_c5_freq( sample_data, freq);
+ sm->set_c5_freq( sample_data, CPTables::get_linear_frequency(CPTables::get_linear_period(note_offset<<1,finetune)) );
+ //printf("NOTE %i,fine %i\n",note_offset,finetune);
+
+ char auxb;
+ auxb=file->get_byte(); //reserved?
+ file->get_byte_array((uint8_t*)instrname,22);
+ sample.set_name(instrname);
+
+ sample_index[j]=s_idx;
+ }
+
+ /*SAMPLE __DATA__!!*/
+
+ for (int j=0;j<sampnum;j++) {
+
+ if (sample_index[j]==-1) continue;
+
+ CPSample *sample=song->get_sample(sample_index[j]);
+ CPSample_ID sid=sample->get_sample_data();
+
+ sm->lock_data(sid);
+
+ void*dataptr=sm->get_data(sid);
+
+ if (sm->is_16bits( sid)) {
+
+ int16_t old=0;
+
+
+ for (int k=0;k<sm->get_size(sid);k++) {
+
+ int16_t newsample;
+ int16_t sampleval=file->get_word();
+ newsample=sampleval+old;
+ old=newsample;
+
+ ((int16_t*)dataptr)[k]=newsample;
+ //sm->set_data( sid, k, newsample );
+ }
+ } else {
+
+ int8_t old=0;
+
+
+ for (int k=0;k<sm->get_size(sid);k++) {
+
+ int8_t newsample;
+ int8_t sampleval=file->get_byte();
+ newsample=sampleval+old;
+ old=newsample;
+
+ ((int8_t*)dataptr)[k]=newsample;
+
+ //sm->set_data( sid, k, (int16_t)newsample << 8 );
+
+ }
+ }
+
+ sm->unlock_data(sid);
+
+ }
+
+ for (int j=0;j<96;j++) {
+
+ int val=notenumb[j];
+ if ((val<0) || (val>15)) continue;
+ else val=sample_index[val];
+ if (val==-1) continue;
+ p_instr->set_sample_number( 12+j,val );
+ }
+
+
+ return FILE_OK;
+}
+
+
+
+CPLoader::Error CPLoader_XM::load_sample(const char *p_file,CPSample *p_sample) {
+
+ return FILE_UNRECOGNIZED;
+}
+
+
+/* Compute CPInstrument Info */
+CPLoader::Error CPLoader_XM::load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx) {
+
+ if ( file->open(p_file,CPFileAccessWrapper::READ) ) return FILE_CANNOT_OPEN;
+ //int i;
+ song=p_song;
+ CPInstrument& instr=*p_song->get_instrument( p_instr_idx );
+ int aux;
+
+
+ char buffer[500];
+ file->get_byte_array((uint8_t*)buffer,0x15);
+ buffer[8]=0;
+ if ( buffer[0]!='E' ||
+ buffer[1]!='x' ||
+ buffer[2]!='t' ||
+ buffer[3]!='e' ||
+ buffer[4]!='n' ||
+ buffer[5]!='d' ||
+ buffer[6]!='e' ||
+ buffer[7]!='d') {
+ file->close();
+ return FILE_UNRECOGNIZED;
+ }
+
+ file->get_byte_array((uint8_t*)buffer,0x16);
+ buffer[0x16]=0;
+ instr.set_name(buffer);
+ aux=file->get_byte(); //says ignore ti
+ /*if(aux!=0x1a) { I'm not sure. this is supposed to be ignored...
+
+ file->close();
+ return FILE_UNRECOGNIZED;
+ } */
+
+ file->get_byte_array((uint8_t*)buffer,0x14); //somethingaboutthename
+ aux=file->get_word(); //version or blahblah
+
+ if (load_instrument_internal(&instr,true,0,0)) {
+
+ file->close();
+ return FILE_CORRUPTED;
+ }
+
+ file->close(); //ook, we got it..
+
+
+ return FILE_OK;
+
+}
+
+
+
+CPLoader_XM::CPLoader_XM(CPFileAccessWrapper *p_file){
+
+ file=p_file;
+}
+CPLoader_XM::~CPLoader_XM(){
+}
+
diff --git a/modules/chibi/cp_loader_xm.h b/modules/chibi/cp_loader_xm.h
new file mode 100644
index 0000000000..9ae480cc8f
--- /dev/null
+++ b/modules/chibi/cp_loader_xm.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_loader_xm.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_LOADER_XM_H
+#define CP_LOADER_XM_H
+
+#include "cp_loader.h"
+
+
+/**
+ *@author red
+ */
+
+
+
+
+class CPLoader_XM : public CPLoader {
+
+
+ struct XM_Header {
+
+ uint8_t idtext[18];
+ uint8_t songname[21];
+ uint8_t hex1a; // ?
+ uint8_t trackername[21];
+ uint16_t version;
+ uint32_t headersize; //from here
+
+ uint16_t songlength; //pattern ordertable
+ uint16_t restart_pos;
+ uint16_t channels_used;
+ uint16_t patterns_used;
+ uint16_t instruments_used;
+ uint16_t use_linear_freq;
+ uint16_t tempo;
+ uint16_t speed;
+ uint8_t orderlist[256];
+
+ } header;
+
+ CPFileAccessWrapper *file;
+
+ Error load_instrument_internal(CPInstrument *pint,bool p_xi,int p_cpos, int p_hsize, int p_sampnumb=-1);
+ CPSong *song;
+
+public:
+
+ bool can_load_song() { return true; }
+ bool can_load_sample() { return false; }
+ bool can_load_instrument() { return true; }
+
+ Error load_song(const char *p_file,CPSong *p_song,bool p_sampleset);
+ Error load_sample(const char *p_file,CPSample *p_sample);
+ Error load_instrument(const char *p_file,CPSong *p_song,int p_instr_idx);
+
+
+ CPLoader_XM(CPFileAccessWrapper *p_file);
+ ~CPLoader_XM();
+};
+
+
+
+#endif
diff --git a/modules/chibi/cp_mixer.h b/modules/chibi/cp_mixer.h
new file mode 100644
index 0000000000..7ad22ac146
--- /dev/null
+++ b/modules/chibi/cp_mixer.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* cp_mixer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_MIXER_H
+#define CP_MIXER_H
+
+#include "cp_sample_defs.h"
+
+/**Abstract base class representing a mixer
+ *@author Juan Linietsky
+ */
+
+
+/******************************
+ mixer.h
+ ----------
+
+Abstract base class for the mixer.
+This is what the player uses to setup
+voices and stuff.. this way
+it can be abstracted to hardware
+devices or other stuff..
+********************************/
+
+class CPSample_ID; /* need this */
+
+class CPMixer {
+public:
+
+ enum {
+
+ FREQUENCY_BITS=8
+
+ };
+
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+ /* Callback */
+
+ virtual void set_callback_interval(int p_interval_us)=0; //in usecs, for tracker it's 2500000/tempo
+ virtual void set_callback(void (*p_callback)(void*),void *p_userdata)=0;
+
+ /* Voice Control */
+
+ virtual void setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) =0;
+ virtual void stop_voice(int p_voice_index) =0;
+ virtual void set_voice_frequency(int p_voice_index,int32_t p_freq) =0; //in freq*FREQUENCY_BITS
+ virtual void set_voice_panning(int p_voice_index,int p_pan) =0;
+ virtual void set_voice_volume(int p_voice_index,int p_vol) =0;
+ virtual void set_voice_filter(int p_filter,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance )=0;
+ virtual void set_voice_reverb_send(int p_voice_index,int p_reverb)=0;
+ virtual void set_voice_chorus_send(int p_voice_index,int p_chorus)=0; /* 0 - 255 */
+
+ virtual void set_reverb_mode(ReverbMode p_mode)=0;
+ virtual void set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10)=0;
+
+
+ /* Info retrieving */
+
+ virtual int32_t get_voice_sample_pos_index(int p_voice_index) =0;
+ virtual int get_voice_panning(int p_voice_index) =0;
+ virtual int get_voice_volume(int p_voice_index) =0;
+ virtual CPSample_ID get_voice_sample_id(int p_voice_index) =0;
+ virtual bool is_voice_active(int p_voice_index) =0;
+ virtual int get_active_voice_count()=0;
+ virtual int get_total_voice_count()=0;
+
+
+ virtual uint32_t get_mix_frequency()=0; //if mixer is not software, return 0
+
+ /* Methods below only work with software mixers, meant for software-based sound drivers, hardware mixers ignore them */
+ virtual int32_t process(int32_t p_frames)=0; /* Call this to process N frames, returns how much it was processed */
+ virtual int32_t *get_mixdown_buffer_ptr()=0; /* retrieve what was mixed */
+ virtual void set_mix_frequency(int32_t p_mix_frequency)=0;
+
+ virtual ~CPMixer() {}
+};
+
+#endif
diff --git a/modules/chibi/cp_note.h b/modules/chibi/cp_note.h
new file mode 100644
index 0000000000..5cfa3f11ec
--- /dev/null
+++ b/modules/chibi/cp_note.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* cp_note.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_NOTE_H
+#define CP_NOTE_H
+
+#include "cp_config.h"
+
+struct CPNote {
+
+ enum {
+
+ NOTES=120,
+ OFF=254,
+ CUT=253,
+ EMPTY=255,
+ SCRIPT=252,
+ };
+
+
+ uint8_t note;
+ uint8_t instrument;
+ uint8_t volume;
+ uint8_t command;
+ uint8_t parameter;
+ unsigned int script_source_sign;
+ bool cloned;
+
+ void clear() {
+
+ note=EMPTY;
+ instrument=EMPTY;
+ volume=EMPTY;
+ command=EMPTY;
+ parameter=0;
+ script_source_sign='\0';
+ cloned=false;
+ }
+
+ void raise() {
+
+ if (note<(NOTES-1))
+ note++;
+ else if (note==SCRIPT && parameter<0xFF)
+ parameter++;
+ }
+
+ void lower() {
+
+ if ((note>0) && (note<NOTES))
+ note--;
+ else if (note==SCRIPT && parameter>0)
+ parameter--;
+
+ }
+
+ bool operator== (const CPNote &rvalue) {
+
+ return (
+ (note==rvalue.note) &&
+ (instrument==rvalue.instrument) &&
+ (volume==rvalue.volume) &&
+ (command==rvalue.command) &&
+ (parameter==rvalue.parameter)
+ );
+ }
+
+ bool is_empty() const { return (note==EMPTY && instrument==EMPTY && volume==EMPTY && command==EMPTY && parameter==0 && !cloned); }
+ CPNote() {
+
+ clear();
+ }
+};
+
+
+#endif
+
diff --git a/modules/chibi/cp_order.h b/modules/chibi/cp_order.h
new file mode 100644
index 0000000000..03ecc00bba
--- /dev/null
+++ b/modules/chibi/cp_order.h
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* cp_order.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_ORDER_H
+#define CP_ORDER_H
+
+
+#include "cp_config.h"
+
+enum CPOrderType {
+ CP_ORDER_NONE=255,
+ CP_ORDER_BREAK=254
+};
+
+typedef uint8_t CPOrder;
+
+#endif
+
diff --git a/modules/chibi/cp_pattern.cpp b/modules/chibi/cp_pattern.cpp
new file mode 100644
index 0000000000..83e165bf87
--- /dev/null
+++ b/modules/chibi/cp_pattern.cpp
@@ -0,0 +1,574 @@
+/*************************************************************************/
+/* cp_pattern.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_pattern.h"
+
+void CPPattern::clear() {
+
+ if (event_count>0) {
+
+
+ CP_FREE(events);
+ events=NULL;
+ event_count=0;
+ }
+
+ length=DEFAULT_LEN;
+
+}
+
+
+bool CPPattern::resize_event_list_to(uint32_t p_events) {
+
+
+ //Module is slow in some cpus, so this should be fast enough
+ uint32_t new_size=((p_events-1)&(~((1<<RESIZE_EVERY_BITS)-1)))+(1<<RESIZE_EVERY_BITS);
+
+ CP_ERR_COND_V(new_size<p_events,true); //bugARM_INFO
+
+ if (event_count==0 && new_size==0)
+ return false; //nothing to do
+
+ if (event_count==0) {
+
+ events=(Event*)CP_ALLOC( new_size*sizeof(Event) );
+
+ } else if (new_size==0) {
+
+ CP_FREE(events);
+ events=NULL;
+ } else {
+
+ CP_ERR_COND_V(events==NULL,true);
+ events=(Event*)CP_REALLOC(events, new_size*sizeof(Event));
+
+ }
+
+ event_count=p_events;
+
+ return false;
+}
+
+
+int32_t CPPattern::get_event_pos(uint16_t p_target_pos) {
+
+
+ if (event_count==0)
+ return -1;
+
+ int low = 0;
+ int high = event_count -1;
+ int middle;
+
+ while( low <= high )
+ {
+ middle = ( low + high ) / 2;
+
+ if( p_target_pos == events[middle].pos ) { //match
+ break;
+ } else if( p_target_pos < events[middle].pos )
+ high = middle - 1; //search low end of array
+ else
+ low = middle + 1; //search high end of array
+ }
+
+ /* adapt so we are behind 2 */
+
+ if (events[middle].pos<p_target_pos)
+ middle++;
+ return middle;
+
+ /* Linear search for now */
+
+ /*
+ int32_t pos_idx=0;
+
+ for (;pos_idx<event_count;pos_idx++) {
+
+ if (event_list[pos_idx].pos>=p_target_pos)
+ break;
+
+ } */
+
+ //return pos_idx;
+}
+
+bool CPPattern::erase_event_at_pos(uint16_t p_pos) {
+
+ if (event_count==0)
+ return false;
+
+
+
+ Event *event_list=events;
+
+ int32_t pos_idx = get_event_pos(p_pos);
+ if (pos_idx==-1) {
+ CP_ERR_COND_V(pos_idx==-1,true);
+ }
+
+ if (pos_idx==event_count || event_list[pos_idx].pos!=p_pos) {
+ /* Nothing to Erase */
+ return false;
+ }
+
+ for (int32_t i=pos_idx;i<(event_count-1);i++) {
+
+ event_list[i]=event_list[i+1];
+ }
+
+
+ resize_event_list_to(event_count-1);
+
+ return false;
+}
+
+bool CPPattern::set_note(uint8_t p_column, uint16_t p_row,const CPNote& p_note) {
+
+ CP_ERR_COND_V(p_column>=WIDTH,true);
+ CP_ERR_COND_V(p_row>=length,true);
+
+ int32_t new_pos;
+ uint16_t target_pos=p_row*WIDTH+p_column;
+
+
+
+ if (p_note.is_empty()) {
+ bool res=erase_event_at_pos(target_pos);
+
+ return res;;
+ }
+
+ Event *event_list=0;
+
+ if (event_count==0) {
+ /* If no events, create the first */
+
+ if (resize_event_list_to(1)) {
+
+ CP_PRINTERR("Can't resize event list to 1");
+ return true;
+ }
+
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ new_pos=0;
+
+ } else {
+ /* Prepare to add */
+
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ int32_t pos_idx = get_event_pos(target_pos);
+
+ if (pos_idx==-1) {
+
+
+ CP_PRINTERR("Can't find add position");
+ return true;
+ }
+
+
+ if (pos_idx==event_count || event_list[pos_idx].pos!=target_pos) {
+ /* If the note being modified didnt exist, then we add it */
+
+ //resize, and return if out of mem
+ if (resize_event_list_to( event_count+1)) {
+
+
+ CP_PRINTERR("Can't resize event list");
+ return true;
+ }
+ event_list=events;
+ if (event_list==0) {
+
+
+ CP_PRINTERR("Can't get event list");
+ return true;
+ }
+
+ //make room for new pos, this wont do a thing if pos_idx was ==event_count
+ for(int32_t i=(event_count-1);i>pos_idx;i--) {
+ event_list[i]=event_list[i-1];
+
+ }
+
+ } /* Else it means that position is taken, so we just modify it! */
+
+
+ new_pos=pos_idx;
+ }
+
+ event_list[new_pos].pos=target_pos;
+ event_list[new_pos].note=p_note.note;
+ event_list[new_pos].instrument=p_note.instrument;
+ event_list[new_pos].volume=p_note.volume;
+ event_list[new_pos].command=p_note.command;
+ event_list[new_pos].parameter=p_note.parameter;
+ event_list[new_pos].script_source_sign=p_note.script_source_sign;
+ event_list[new_pos].cloned=p_note.cloned;
+
+
+
+
+ return false;
+
+}
+CPNote CPPattern::get_note(uint8_t p_column,uint16_t p_row) {
+
+ if (p_column==CPNote::EMPTY) return CPNote();
+
+ CP_ERR_COND_V(p_column>=WIDTH,CPNote());
+ CP_ERR_COND_V(p_row>=length,CPNote());
+
+ if (event_count==0)
+ return CPNote();
+
+
+ Event *event_list=events;
+
+ CP_ERR_COND_V(event_list==0,CPNote());
+
+ uint16_t target_pos=p_row*WIDTH+p_column;
+ int32_t pos_idx = get_event_pos(target_pos);
+ if (pos_idx==-1) {
+
+ CP_PRINTERR("Can't find event pos");
+ return CPNote();
+ }
+
+ if (pos_idx>=event_count || event_list[pos_idx].pos!=target_pos) {
+ /* no note found */
+
+ return CPNote();
+ }
+
+ CPNote n;
+ n.note=event_list[pos_idx].note;
+ n.instrument=event_list[pos_idx].instrument;
+ n.volume=event_list[pos_idx].volume;
+ n.command=event_list[pos_idx].command;
+ n.parameter=event_list[pos_idx].parameter;
+ n.script_source_sign=event_list[pos_idx].script_source_sign;
+ n.cloned=event_list[pos_idx].cloned;
+
+
+ return n;
+
+}
+
+CPNote CPPattern::get_transformed_script_note(uint8_t p_column,uint16_t p_row ) {
+
+ CPNote n = get_note( p_column, p_row );
+
+ // get source channel and note
+
+ int channel = get_scripted_note_target_channel( p_column, p_row );
+ CPNote src_n = get_note( channel, 0 );
+
+ if ( src_n.note == CPNote::SCRIPT ) return CPNote();
+
+ script_transform_note( src_n, n );
+
+ return src_n;
+
+}
+
+int CPPattern::get_scripted_note_target_channel(uint8_t p_column, uint16_t p_row) {
+
+ CPNote n = get_note( p_column, p_row );
+
+ if ( n.note != CPNote::SCRIPT ) return CPNote::EMPTY;
+
+ int channel = n.instrument;
+
+ if ( n.script_source_sign == '\0' ) {
+
+ if ( channel < 0 || channel >= CPPattern::WIDTH ) return CPNote::EMPTY;
+
+ } else {
+
+ channel = p_column + ( ( n.script_source_sign=='+') ? 1 : -1 ) * (channel+1);
+ if ( channel < 0 || channel >= CPPattern::WIDTH ) return CPNote::EMPTY;
+
+ }
+
+ return channel;
+}
+
+void CPPattern::scripted_clone(uint8_t p_column, uint16_t p_row) {
+
+ int channel = get_scripted_note_target_channel( p_column, p_row );
+ int src_row = 1;
+ CPNote script_n = get_note( p_column, p_row );
+
+ for ( int row = p_row+1; row < length; ++row ) {
+
+ CPNote src_n = get_note( channel, src_row );
+ CPNote target_n = get_note( p_column, row );
+
+ if ( target_n.note != CPNote::SCRIPT ) {
+ if ( src_n.note == CPNote::SCRIPT ) {
+ src_n = CPNote();
+ channel = CPNote::EMPTY;
+ }
+
+ script_transform_note( src_n, script_n );
+
+ src_n.cloned = true;
+ set_note( p_column, row, src_n );
+
+ } else {
+
+ return;
+
+ }
+
+ src_row++;
+ }
+
+}
+
+void CPPattern::scripted_clone_remove(uint8_t p_column, uint16_t p_row) {
+
+ if ( get_note( p_column, p_row ).cloned )
+ set_note( p_column, p_row, CPNote() );
+
+ for ( int row = p_row+1; row < length; ++row ) {
+
+ CPNote target_n = get_note( p_column, row );
+
+ if ( target_n.note != CPNote::SCRIPT ) {
+
+ set_note( p_column, row, CPNote() );
+
+ } else {
+
+ return;
+
+ }
+
+ }
+
+}
+
+void CPPattern::script_transform_note(CPNote& n, const CPNote& p_note) {
+
+ // set instrument
+
+ if ( n.note < CPNote::NOTES && p_note.volume != CPNote::EMPTY ) {
+
+ n.instrument = p_note.volume;
+
+ }
+
+ // transpose
+
+ if ( n.note < CPNote::NOTES && p_note.command != CPNote::EMPTY ) {
+
+ int transpose = ( p_note.parameter & 0xF ) + ( p_note.parameter / 0x10 ) * 12;
+
+ if ( p_note.command == '^' ) {
+
+ if ( n.note >= CPNote::NOTES-transpose )
+ n.note = CPNote::NOTES-1;
+ else
+ n.note += transpose;
+
+ } else if ( p_note.command == 'v' ) {
+
+ if ( n.note <= transpose )
+ n.note = 0;
+ else
+ n.note -= transpose;
+
+ }
+ }
+
+}
+
+bool CPPattern::update_scripted_clones_sourcing_channel( int channel ) {
+
+ bool updated = false;
+
+ for ( int x = 0; x < WIDTH; ++x ) {
+
+ for (int y = 0; y < length; ++y ) {
+
+ if ( channel == get_scripted_note_target_channel( x, y ) ) {
+
+ scripted_clone( x, y );
+ updated = true;
+ }
+
+ }
+
+ }
+
+ return updated;
+}
+
+void CPPattern::set_length(uint16_t p_rows) {
+
+
+
+ if (event_count==0) {
+
+ if (p_rows>=MIN_ROWS)
+ length=p_rows;
+
+
+ return;
+
+ }
+
+ if (p_rows<MIN_ROWS) {
+
+ return;
+ }
+
+ if (p_rows<length) {
+
+ Event* event_list=events;
+ if (event_list==0) {
+
+ CP_PRINTERR("get_event_list() Failed");
+ return;
+ }
+
+
+ uint16_t target_pos=p_rows*WIDTH;
+ int32_t pos_idx = get_event_pos(target_pos);
+
+
+ if (pos_idx==-1) {
+
+ CP_ERR_COND(pos_idx==-1);
+ }
+
+ if (resize_event_list_to(pos_idx)) {
+
+ CP_PRINTERR("resize_event_list_to(pos_idx) Failed");
+ return;
+ }
+
+ }
+
+ length=p_rows;
+
+
+}
+#if 0
+void CPPattern::copy_to(CPPattern *p_pattern) const {
+
+
+
+
+ p_pattern->clear();
+ p_pattern->length=length;
+
+
+ if (!event_count)
+ return;
+
+
+
+ int bufsiz=MemPool_Wrapper::get_singleton()->get_mem_size( mem_handle );
+ MemPool_Handle aux_mem_handle=MemPool_Wrapper::get_singleton()->alloc_mem( bufsiz );
+
+ if (aux_mem_handle.is_null()) {
+
+ CP_PRINTERR("own handle is null");
+
+ return;
+ }
+
+
+ if (MemPool_Wrapper::get_singleton()->lock_mem(aux_mem_handle)) {
+ CP_PRINTERR("Unable to lock aux new handle");
+
+ return;
+
+ }
+
+ if (MemPool_Wrapper::get_singleton()->lock_mem(mem_handle)) {
+
+ CP_PRINTERR("Unable to lock own handle");
+
+ return;
+ }
+
+ uint8_t* srcuint8_tt8_t*)MemPool_Wrapper::get_singleton()->get_mem(mem_handle);
+ uint8_t* dstuint8_tt8_t*)MemPool_Wrapper::get_singleton()->get_mem(aux_mem_handle);
+
+ for (int i=0;i<bufsiz;i++)
+ dst[i]=src[i];
+
+ MemPool_Wrapper::get_singleton()->unlock_mem(mem_handle);
+ MemPool_Wrapper::get_singleton()->unlock_mem(aux_mem_handle);
+
+ p_pattern->mem_handle=aux_mem_handle;
+ p_pattern->event_count=event_count;
+
+
+}
+#endif
+uint16_t CPPattern::get_length() {
+
+
+ return length;
+}
+CPPattern::CPPattern() {
+
+
+ length=DEFAULT_LEN;
+ event_count=0;
+ clear();
+
+}
+bool CPPattern::is_empty() {
+
+ return events==NULL;
+}
+
+CPPattern::~CPPattern() {
+
+ clear();
+}
diff --git a/modules/chibi/cp_pattern.h b/modules/chibi/cp_pattern.h
new file mode 100644
index 0000000000..4065caa5e5
--- /dev/null
+++ b/modules/chibi/cp_pattern.h
@@ -0,0 +1,94 @@
+/*************************************************************************/
+/* cp_pattern.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_PATTERN_H
+#define CP_PATTERN_H
+
+#include "cp_note.h"
+
+class CPPattern {
+public:
+
+
+ enum {
+ WIDTH=64,
+ DEFAULT_LEN=64,
+ RESIZE_EVERY_BITS=4,
+ MIN_ROWS=1, //otherwise clipboard wont work
+ MAX_LEN=256
+
+ };
+
+private:
+ struct Event {
+
+ uint16_t pos; //column*WIDTH+row
+ uint8_t note;
+ uint8_t instrument;
+ uint8_t volume;
+ uint8_t command;
+ uint8_t parameter;
+ unsigned int script_source_sign;
+ bool cloned;
+ };
+
+ uint16_t length;
+ uint32_t event_count;
+ Event* events;
+
+ int32_t get_event_pos(uint16_t p_target_pos);
+ bool erase_event_at_pos(uint16_t p_pos);
+
+ bool resize_event_list_to(uint32_t p_events);
+
+ void operator=(const CPPattern& p_pattern); //no operator=
+public:
+
+ bool is_empty();
+ void clear();
+
+ bool set_note(uint8_t p_column, uint16_t p_row,const CPNote& p_note); //true if no more memory
+ CPNote get_note(uint8_t p_column,uint16_t p_row);
+
+ CPNote get_transformed_script_note(uint8_t p_column, uint16_t p_row);
+ int get_scripted_note_target_channel(uint8_t p_column, uint16_t p_row);
+ void scripted_clone(uint8_t p_column, uint16_t p_row);
+ void scripted_clone_remove(uint8_t p_column, uint16_t p_row);
+ void script_transform_note(CPNote& n, const CPNote& p_note);
+ bool update_scripted_clones_sourcing_channel(int channel);
+
+ //void copy_to(CPPattern *p_pattern) const;
+ void set_length(uint16_t p_rows);
+ uint16_t get_length();
+ CPPattern();
+ ~CPPattern();
+
+
+};
+
+#endif
diff --git a/modules/chibi/cp_player_data.cpp b/modules/chibi/cp_player_data.cpp
new file mode 100644
index 0000000000..3f3e9a5202
--- /dev/null
+++ b/modules/chibi/cp_player_data.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* cp_player_data.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include <stdio.h>
+
+
+CPPlayer::CPPlayer(CPMixer *p_mixer,CPSong *p_song){
+
+ song=p_song;
+ mixer=p_mixer;
+ control.max_voices=p_mixer->get_total_voice_count()-1; //leave one for the sample
+ control.force_no_nna=false;
+ control.external_vibrato=false;
+ control.filters=true;
+ control.random_seed=128364; //anything
+ control.play_mode=0;
+ set_virtual_channels(p_mixer->get_total_voice_count());
+ mixer->set_callback( &CPPlayer::callback_function, this );
+
+ reset();
+}
+CPPlayer::~CPPlayer(){
+}
+
+void CPPlayer::set_virtual_channels(int p_amount) {
+
+ if (p_amount<1) return;
+ if (p_amount>mixer->get_total_voice_count())
+ return;
+
+ control.max_voices=p_amount;
+
+}
+
+
+void CPPlayer::callback_function(void *p_userdata) {
+
+ CPPlayer*pd=(CPPlayer*)p_userdata;
+ pd->process_tick();
+
+}
+
+void CPPlayer::process_tick() {
+
+ handle_tick();
+ mixer->set_callback_interval( 2500000/control.tempo );
+ song_usecs+=2500000/control.tempo;
+}
+
+void CPPlayer::reset() {
+
+ if ( mixer==NULL ) return ;
+ if ( song==NULL ) return ;
+
+ int i;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ voice[i].reset();
+ mixer->stop_voice(i);
+ }
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ control.channel[i].reset();
+ control.channel[i].channel_volume=song->get_channel_volume(i);
+ control.channel[i].channel_panning=((int)song->get_channel_pan( i)*PAN_RIGHT/64);
+ if (song->is_channel_surround(i))
+ control.channel[i].channel_panning=PAN_SURROUND;
+ control.channel[i].mute=song->is_channel_mute( i );
+ control.channel[i].chorus_send=song->get_channel_chorus(i)*0xFF/64;
+ control.channel[i].reverb_send=song->get_channel_reverb(i)*0xFF/64;
+ }
+
+
+ control.speed=song->get_speed();
+ control.tempo=song->get_tempo();
+ control.global_volume=song->get_global_volume();
+
+ control.position.current_pattern=0;
+ control.position.current_row=0;
+ control.position.current_order=0;
+ control.position.force_next_order=-1;
+ control.ticks_counter=control.speed;
+ control.position.forbid_jump=false;
+
+ song_usecs=0;
+
+}
+
+int64_t CPPlayer::get_channel_last_note_time_usec(int p_channel) const {
+
+ CP_FAIL_INDEX_V(p_channel,64,-1);
+ return control.channel[p_channel].last_event_usecs;
+
+}
+
+void CPPlayer::set_channel_global_volume(int p_channel,int p_volume) {
+
+ CP_FAIL_INDEX(p_channel,64);
+ control.channel[p_channel].channel_global_volume=CLAMP(p_volume,0,255);
+
+}
+
+int CPPlayer::get_channel_global_volume(int p_channel) const{
+
+ CP_FAIL_INDEX_V(p_channel,64,-1);
+ return control.channel[p_channel].channel_global_volume;
+
+}
+
+bool CPPlayer::reached_end_of_song() {
+
+ return control.reached_end;
+
+}
+void CPPlayer::set_force_external_vibratos(bool p_force) {
+
+ control.external_vibrato=p_force;
+}
+void CPPlayer::set_force_no_nna(bool p_force) {
+
+ control.force_no_nna=p_force;
+}
diff --git a/modules/chibi/cp_player_data.h b/modules/chibi/cp_player_data.h
new file mode 100644
index 0000000000..282592b8f4
--- /dev/null
+++ b/modules/chibi/cp_player_data.h
@@ -0,0 +1,582 @@
+/*************************************************************************/
+/* cp_player_data.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CP_PLAYER_DATA_H
+#define CP_PLAYER_DATA_H
+
+#include "cp_config.h"
+#include "cp_song.h"
+#include "cp_mixer.h"
+#include "cp_tables.h"
+
+/**CPPlayer Data
+ *@author Juan Linietsky
+ */
+
+/******************************
+ player_data.h
+ ------------------------
+
+The player and its data.
+I hope you dont get sick reading this
+********************************/
+
+//Default pan values
+
+
+class CPPlayer {
+
+ enum {
+ PAN_SURROUND=512,
+ PAN_RIGHT=255,
+ PAN_LEFT=0,
+ PAN_CENTER=128
+ };
+
+
+ CPSong *song;
+
+ CPMixer *mixer;
+
+ struct Filter_Control {
+
+ int32_t it_reso;
+ int32_t it_cutoff;
+ int32_t envelope_cutoff;
+ int32_t final_cutoff;
+
+ void process();
+ void set_filter_parameters(int *p_cutoff,uint8_t *p_reso);
+
+ };
+
+ //tells you if a channel is doing
+ //noteoff/notekill/notefade/etc
+ enum {
+
+ END_NOTE_NOTHING=0,
+ END_NOTE_OFF=1,
+ END_NOTE_FADE=2,
+ END_NOTE_KILL=4
+ };
+
+ //Tells you what should a channel restart
+ enum {
+
+ KICK_NOTHING,
+ KICK_NOTE,
+ KICK_NOTEOFF,
+ KICK_ENVELOPE
+ };
+
+ enum {
+
+ MAX_VOICES=256
+ };
+
+ struct Channel_Control;
+
+ struct Voice_Control {
+
+ struct Envelope_Control {
+
+ int pos_index;
+ int status;
+ int value;
+ bool sustain_looping;
+ bool looping;
+ bool terminated;
+ bool active;
+ bool kill;
+
+ };
+
+ Filter_Control filter;
+ uint16_t reverb_send;
+ uint16_t chorus_send;
+
+ CPInstrument* instrument_ptr;
+ CPSample* sample_ptr;
+
+// Sample_Data *sample_data;
+
+ int32_t period;
+
+ int32_t sample_start_index; /* The starting byte index in the sample */
+
+ bool has_master_channel;
+ int master_channel_index;
+ int instruement_index;
+
+ int instrument_index;
+ int sample_index;
+ int8_t NNA_type;
+
+ int note_end_flags;
+
+ uint8_t sample; /* which instrument number */
+
+ int16_t output_volume; /* output volume (vol + sampcol + instvol) */
+ int8_t channel_volume; /* channel's "global" volume */
+ uint16_t fadeout_volume; /* fading volume rate */
+ int32_t total_volume; /* total volume of channel (before global mixings) */
+ uint8_t kick; /* if true = sample has to be restarted */
+
+ uint8_t note; /* the audible note (as heard, direct rep of period) */
+
+ int16_t panning; /* panning position */
+
+ uint8_t nna; /* New note action type + master/slave flags */
+ uint8_t volflg; /* volume envelope settings */
+ uint8_t panflg; /* panning envelope settings */
+ uint8_t pitflg; /* pitch envelope settings */
+ uint8_t keyoff; /* if true = fade out and stuff */
+ int16_t handle; /* which sample-handle */
+ int32_t start; /* The start byte index in the sample */
+
+ /* Below here is info NOT in MP_CONTROL!! */
+ //ENVPR venv;
+ //ENVPR penv;
+ //ENVPR cenv;
+
+ Envelope_Control volume_envelope_ctrl;
+ Envelope_Control panning_envelope_ctrl;
+ Envelope_Control pitch_envelope_ctrl;
+
+ uint16_t auto_vibrato_pos; /* autovibrato pos */
+ uint16_t auto_vibrato_sweep_pos; /* autovibrato sweep pos */
+
+ int16_t masterchn;
+ uint16_t masterperiod;
+
+ Channel_Control* master_channel; /* index of "master" effects channel */
+
+ void start_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl,Envelope_Control *p_from_env);
+ bool process_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl);
+
+ uint16_t display_volume;
+
+ Voice_Control() {
+
+ reset();
+ }
+
+ void reset();
+ void update_info_from_master_channel();
+
+
+ };
+
+
+ struct Channel_Control {
+
+ /* NOTE info */
+ uint8_t note; /* the audible note as heard, direct rep of period */
+ uint8_t real_note; /* the note that indexes the audible */
+ int32_t sample_start_index; /* The starting byte index in the sample */
+ uint8_t old_note;
+
+ uint8_t kick;
+
+ Filter_Control filter;
+ uint16_t reverb_send;
+ uint16_t chorus_send;
+
+
+ int note_end_flags;
+
+ /* INSTRUMENT INFO */
+
+ CPInstrument* instrument_ptr;
+ CPSample* sample_ptr;
+
+ uint8_t instrument_index;
+ uint8_t sample_index;
+ bool new_instrument;
+
+ /* SAMPLE SPECIFIC INFO */
+ int32_t base_speed; /* what finetune to use */
+
+ /* INSTRUMENT SPECIFIC INFO */
+
+ int8_t NNA_type;
+ int8_t duplicate_check_type;
+ int8_t duplicate_check_action;
+
+ bool volume_envelope_on;
+ bool panning_envelope_on;
+ bool pitch_envelope_on;
+
+ bool has_own_period;
+
+ bool row_has_note;
+
+ /* VOLUME COLUMN */
+
+ int16_t volume; /* amiga volume (0 t/m 64) to play the sample at */
+ int16_t aux_volume;
+ bool has_own_volume;
+ bool mute;
+ int16_t random_volume_variation; /* 0-100 - 100 has no effect */
+
+ /* VOLUME/PAN/PITCH MODIFIERS */
+
+ int8_t default_volume; // CHANNEL default volume (0-64)
+ int16_t channel_volume; // CHANNEL current volume //chanvol - current!
+ int16_t output_volume; /* output volume (vol + sampcol + instvol) //volume */
+ int16_t channel_global_volume;
+
+ uint16_t fadeout_volume; /* fading volume rate */
+
+ int32_t period; /* period to play the sample at */
+
+ /* PAN */
+
+ int16_t panning; /* panning position */
+ int16_t channel_panning;
+ int8_t sliding;
+
+ uint16_t aux_period; /* temporary period */
+
+
+
+ /* TIMING */
+ uint8_t note_delay; /* (used for note delay) */
+
+ /* Slave Voice Control */
+
+ Voice_Control *slave_voice; /* Audio Slave of current effects control channel */
+
+ struct Carry {
+
+ Voice_Control::Envelope_Control vol;
+ Voice_Control::Envelope_Control pan;
+ Voice_Control::Envelope_Control pitch;
+ bool maybe;
+
+ } carry;
+
+
+
+ uint8_t slave_voice_index; /* Audio Slave of current effects control channel */
+
+ uint8_t* row; /* row currently playing on this channel */
+
+ /* effect memory variables */
+
+ uint8_t current_command;
+ uint8_t current_parameter;
+ uint8_t current_volume_command;
+ uint8_t current_volume_parameter;
+ uint8_t volcol_volume_slide;
+
+ /* CPSample Offset */
+
+ int32_t lo_offset;
+ int32_t hi_offset;
+
+ /* Panbrello waveform */
+ uint8_t panbrello_type; /* current panbrello waveform */
+ uint8_t panbrello_position; /* current panbrello position */
+ int8_t panbrello_speed; /* "" speed */
+ uint8_t panbrello_depth; /* "" depth */
+ uint8_t panbrello_info;
+ /* Arpegio */
+
+ uint8_t arpegio_info;
+ /* CPPattern Loop */
+
+ int pattern_loop_position;
+ int8_t pattern_loop_count;
+
+ /* Vibrato */
+ bool doing_vibrato;
+ int8_t vibrato_position; /* current vibrato position */
+ uint8_t vibrato_speed; /* "" speed */
+ uint8_t vibrato_depth; /* "" depth */
+ uint8_t vibrato_type;
+ /* Tremor */
+ int8_t tremor_position;
+ uint8_t tremor_speed; /* s3m tremor ontime/offtime */
+ uint8_t tremor_depth;
+ uint8_t tremor_info;
+
+ /* Tremolo */
+ int8_t tremolo_position;
+ uint8_t tremolo_speed; /* s3m tremor ontime/offtime */
+ uint8_t tremolo_depth;
+ uint8_t tremolo_info;
+ uint8_t tremolo_type;
+
+ /* Retrig */
+ int8_t retrig_counter; /* retrig value (0 means don't retrig) */
+ uint8_t retrig_speed; /* last used retrig speed */
+ uint8_t retrig_volslide; /* last used retrig slide */
+
+ /* CPSample Offset */
+ int32_t sample_offset_hi; /* last used high order of sample offset */
+ uint16_t sample_offset; /* last used low order of sample-offset (effect 9) */
+ uint16_t sample_offset_fine; /* fine sample offset memory */
+
+ /* Portamento */
+ uint16_t slide_to_period; /* period to slide to (with effect 3 or 5) */
+ uint8_t portamento_speed;
+
+ /* Volume Slide */
+
+ uint8_t volume_slide_info;
+
+ /* Channel Volume Slide */
+
+ uint8_t channel_volume_slide_info;
+
+ /* Global Volume Slide */
+
+ uint8_t global_volume_slide_info;
+
+ /* Channel Pan Slide */
+
+ uint8_t channel_pan_slide_info;
+
+ /* Pitch Slide */
+
+ uint8_t pitch_slide_info;
+ /* Tempo Slide */
+
+ uint8_t tempo_slide_info;
+
+ /* S effects memory */
+
+ uint8_t current_S_effect;
+ uint8_t current_S_data;
+
+ /* Volume column memory */
+
+ uint8_t volume_column_effect_mem;
+ uint8_t volume_column_data_mem;
+
+ int64_t last_event_usecs;
+ bool reserved;
+
+ void reset();
+
+ Channel_Control() { channel_global_volume=255; last_event_usecs=-1; }
+ };
+
+ struct Control_Variables { // control variables (dynamic version) of initial variables
+
+ bool reached_end;
+
+ char play_mode;
+ bool filters;
+ int global_volume;
+ int speed;
+ int tempo;
+
+ int ticks_counter;
+
+ int pattern_delay_1;
+ int pattern_delay_2;
+
+ Channel_Control channel[CPPattern::WIDTH];
+
+ int max_voices;
+
+ int voices_used; /* reference value */
+
+ bool force_no_nna;
+ bool external_vibrato;
+
+ struct Position {
+
+ int current_order;
+ int current_pattern;
+ int current_row;
+ int force_next_order;
+ bool forbid_jump;
+ };
+
+ int32_t random_seed;
+
+ Position position;
+ Position previous_position;
+
+ };
+
+
+ Voice_Control voice[MAX_VOICES];
+
+ Control_Variables control;
+
+ /* VOICE SETUP */
+
+ void setup_voices();
+
+ /* MIXER SETUP */
+ void handle_tick();
+ void update_mixer();
+
+ /* NOTE / INSTRUMENT PROCESSING */
+
+ void process_new_note(int p_track,uint8_t p_note);
+ bool process_new_instrument(int p_track,uint8_t p_instrument);
+ bool process_note_and_instrument(int p_track,int p_note,int p_instrument);
+
+ /* EFFECT PROCESSING */
+ void do_effect_S(int p_track);
+ void do_panbrello(int p_track);
+ void do_global_volume_slide(int p_track);
+ void do_tremolo(int p_track);
+ void do_retrig(int p_track);
+ void do_pan_slide(int p_track);
+ void do_channel_volume_slide(int p_track);
+ void do_volume_slide(int p_track,int inf);
+ void do_pitch_slide_down(int p_track,uint8_t inf);
+ void do_pitch_slide_up(int p_track,uint8_t inf);
+ void do_tremor(int p_track);
+ void do_vibrato(int p_track,bool fine);
+ void do_pitch_slide_to_note(int p_track);
+ void run_effects(int p_track);
+ void run_volume_column_effects(int p_track);
+ void pre_process_effects();
+ void do_arpegio(int p_track);
+ uint64_t song_usecs;
+ /* NNA */
+
+ void process_NNAs();
+
+
+ /* MISC UTILS */
+
+
+ int find_empty_voice();
+ void process_volume_column(int p_track,uint8_t p_volume);
+ void process_note(int p_track,CPNote p_note);
+
+ /* CPTables */
+ static uint8_t auto_vibrato_table[128];
+ static uint8_t vibrato_table[32];
+ static int8_t panbrello_table[256];
+
+ static void callback_function(void *p_userdata);
+
+public:
+ //Play modes
+
+ enum {
+
+ PLAY_NOTHING =0,
+ PLAY_PATTERN =1,
+ PLAY_SONG =2
+ };
+
+
+ int32_t get_frequency(int32_t period);
+ int32_t get_period(uint16_t note,int32_t p_c5freq);
+
+
+ int get_current_tempo() { return control.tempo; };
+ int get_current_speed() { return control.speed; };
+
+ int get_voices_used() { return control.voices_used;};
+ int get_voice_envelope_pos(int p_voice,CPEnvelope *p_envelope);
+ int get_voice_amount_limit() { return control.max_voices; };
+ void set_voice_amount_limit(int p_limit);
+ void set_reserved_voices(int p_amount);
+ int get_reserved_voices_amount();
+
+ bool is_voice_active(int p_voice);
+ int get_channel_voice(int p_channel);
+ const char* get_voice_sample_name(int p_voice);
+ const char* get_voice_instrument_name(int p_voice);
+ CPEnvelope* get_voice_envelope(int p_voice,CPInstrument::EnvelopeType p_env_type);
+ int get_voice_envelope_pos(int p_voice,CPInstrument::EnvelopeType p_env_type);
+ int get_voice_volume(int p_voice);
+
+ int get_voice_sample_index(int p_voice);
+
+ void set_virtual_channels(int p_amount);
+ int get_virtual_channels() { return control.max_voices; };
+
+
+ /* Play Info/Position */
+ bool is_playing() { return (control.play_mode>0); };
+ int get_play_mode() {return (control.play_mode);};
+ int get_current_order() { return control.position.current_order; };
+ int get_current_row() { return control.position.current_row; };
+ int get_current_pattern() { return control.position.current_pattern; };
+
+ void goto_next_order();
+ void goto_previous_order();
+
+ void process_tick();
+
+
+ CPMixer* get_mixer_ptr() {
+
+ return mixer;
+ }
+
+
+ void reset();
+
+
+
+ /* External player control - editor - */
+
+ void play_start_pattern(int p_pattern);
+ void play_start_song();
+ void play_start_song_from_order(int p_order);
+ void play_start_song_from_order_and_row(int p_order,int p_row);
+ void play_start(int p_pattern, int p_order, int p_row,bool p_lock=true);
+
+ void play_stop();
+ void play_note(int p_channel,CPNote note,bool p_reserve=false);
+
+ bool reached_end_of_song();
+
+ void set_force_no_nna(bool p_force);
+ void set_force_external_vibratos(bool p_force);
+
+ void set_filters_enabled(bool p_enable);
+ bool are_filters_enabled() { return control.filters; }
+
+ void set_channel_global_volume(int p_channel,int p_volume); //0-255
+ int get_channel_global_volume(int p_channel) const;
+
+ int64_t get_channel_last_note_time_usec(int p_channel) const;
+
+ CPSong *get_song() { return song; };
+
+
+ CPPlayer(CPMixer *p_mixer,CPSong *p_song);
+ ~CPPlayer();
+};
+
+#endif
diff --git a/modules/chibi/cp_player_data_control.cpp b/modules/chibi/cp_player_data_control.cpp
new file mode 100644
index 0000000000..d9aaed904f
--- /dev/null
+++ b/modules/chibi/cp_player_data_control.cpp
@@ -0,0 +1,324 @@
+/*************************************************************************/
+/* cp_player_data_control.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+void CPPlayer::play_start_pattern(int p_pattern) {
+
+ play_start(p_pattern,-1,-1);
+}
+
+void CPPlayer::play_start_song() {
+
+ play_start(-1,-1,-1);
+}
+
+void CPPlayer::play_start_song_from_order(int p_order) {
+
+ play_start(-1,p_order,-1);
+}
+
+void CPPlayer::play_start_song_from_order_and_row(int p_order,int p_row) {
+
+ play_start(-1,p_order,p_row);
+}
+
+void CPPlayer::play_start(int p_pattern, int p_order, int p_row,bool p_lock) {
+
+
+ if (control.play_mode!=PLAY_NOTHING) play_stop();
+
+
+ reset();
+
+ if (p_pattern!=-1) {
+
+ control.play_mode=PLAY_PATTERN;
+ control.position.current_pattern=p_pattern;
+ control.position.current_row=(p_row!=-1)?p_row:0;
+
+ } else {
+
+ control.position.current_order=get_song_next_order_idx(song,(p_order==-1)?p_order:p_order-1);
+ if (control.position.current_order!=-1) {
+
+ control.play_mode=PLAY_SONG;
+ control.position.current_pattern=song->get_order(control.position.current_order);
+ control.position.current_row=(p_row!=-1)?p_row:0;
+ }
+ }
+
+
+ control.reached_end=(control.play_mode==PLAY_NOTHING);
+
+
+}
+
+void CPPlayer::play_stop() {
+
+ int i;
+
+
+ control.play_mode=PLAY_NOTHING;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ voice[i].reset();
+ mixer->stop_voice(i);
+ }
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ control.channel[i].reset();
+ }
+
+ reset();
+
+}
+
+void CPPlayer::play_note(int p_channel,CPNote note,bool p_reserve) {
+
+
+
+ if (control.play_mode==PLAY_NOTHING) {
+
+ control.ticks_counter=0;
+ }
+
+ /*control.channel[p_channel].reset();
+ control.channel[p_channel].channel_volume=song->get_channel_volume(p_channel);
+ control.channel[p_channel].channel_panning=((int)song->get_channel_pan( p_channel)*255/64);*/
+ if (p_reserve) {
+ control.channel[p_channel].mute=false;
+ control.channel[p_channel].reserved=true;
+ } else {
+
+ control.channel[p_channel].reserved=false;
+
+ }
+ process_note(p_channel,note);
+
+
+
+}
+
+
+int CPPlayer::get_voice_volume(int p_voice) {
+
+ return voice[p_voice].display_volume;
+}
+
+
+int CPPlayer::get_voice_envelope_pos(int p_voice,CPEnvelope *p_envelope) {
+
+ int i,tmp_index=-1;
+
+ i=p_voice;
+
+
+
+
+ if ((song->has_instruments()) && (voice[i].instrument_ptr!=NULL) && (voice[i].fadeout_volume>0)) {
+
+ if ((p_envelope==voice[i].instrument_ptr->get_volume_envelope()) && (voice[i].instrument_ptr->get_volume_envelope()->is_enabled())) {
+
+ tmp_index=voice[i].volume_envelope_ctrl.pos_index;
+ }
+
+ if ((p_envelope==voice[i].instrument_ptr->get_pan_envelope()) && (voice[i].instrument_ptr->get_pan_envelope()->is_enabled())) {
+
+ tmp_index=voice[i].panning_envelope_ctrl.pos_index;
+ }
+
+ if ((p_envelope==voice[i].instrument_ptr->get_pitch_filter_envelope()) && (voice[i].instrument_ptr->get_pitch_filter_envelope()->is_enabled())) {
+
+
+ tmp_index=voice[i].pitch_envelope_ctrl.pos_index;
+ }
+
+ }
+
+
+
+ return tmp_index;
+}
+
+void CPPlayer::goto_next_order() {
+
+
+ if (control.play_mode!=PLAY_SONG) return;
+
+
+
+ control.position.current_row=0;
+
+
+ control.position.current_order=get_song_next_order_idx(song, control.position.current_order);
+
+
+
+ if (control.position.current_order==-1) {
+
+ reset();
+ }
+
+ control.position.current_pattern=song->get_order(control.position.current_order);
+
+
+}
+void CPPlayer::goto_previous_order() {
+
+ if (control.play_mode!=PLAY_SONG) return;
+
+
+ int next_order,current_order;
+
+ control.position.current_row=0;
+
+ current_order=control.position.current_order;
+
+ next_order=get_song_next_order_idx(song, current_order);
+
+ while ((next_order!=control.position.current_order) && (next_order!=-1)) {
+
+ current_order=next_order;
+ next_order=get_song_next_order_idx(song, current_order);
+ }
+
+ if (next_order==-1) {
+
+ reset();
+ } else {
+
+ control.position.current_order=current_order;
+ control.position.current_pattern=song->get_order(control.position.current_order);
+
+ }
+
+
+
+}
+
+int CPPlayer::get_channel_voice(int p_channel) {
+
+ if (control.channel[p_channel].slave_voice==NULL) return -1;
+ else return control.channel[p_channel].slave_voice_index;
+}
+
+const char* CPPlayer::get_voice_sample_name(int p_voice) {
+
+ const char *name = NULL;
+
+
+
+ if (!voice[p_voice].sample_ptr) name=voice[p_voice].sample_ptr->get_name();
+
+
+
+ return name;
+
+}
+
+
+bool CPPlayer::is_voice_active(int p_voice) {
+
+ return !( ((voice[p_voice].kick==KICK_NOTHING)||(voice[p_voice].kick==KICK_ENVELOPE))&&!mixer->is_voice_active(p_voice) );
+
+}
+
+
+
+int CPPlayer::get_voice_envelope_pos(int p_voice,CPInstrument::EnvelopeType p_env_type) {
+
+ if (!is_voice_active(p_voice))
+ return -1;
+
+ Voice_Control::Envelope_Control *env=0;
+
+ switch (p_env_type) {
+
+ case CPInstrument::VOLUME_ENVELOPE: env=&voice[p_voice].volume_envelope_ctrl; break;
+ case CPInstrument::PAN_ENVELOPE: env=&voice[p_voice].panning_envelope_ctrl; break;
+ case CPInstrument::PITCH_ENVELOPE: env=&voice[p_voice].pitch_envelope_ctrl; break;
+
+ }
+
+ if (!env)
+ return -1;
+
+ if (!env->active || env->terminated)
+ return -1;
+
+ return env->pos_index;
+}
+
+
+CPEnvelope* CPPlayer::get_voice_envelope(int p_voice,CPInstrument::EnvelopeType p_env_type) {
+
+ CPInstrument *ins=voice[p_voice].instrument_ptr;
+
+ if (!ins)
+ return 0;
+
+ switch( p_env_type ) {
+
+
+ case CPInstrument::VOLUME_ENVELOPE: return ins->get_volume_envelope();
+ case CPInstrument::PAN_ENVELOPE: return ins->get_pan_envelope();
+ case CPInstrument::PITCH_ENVELOPE: return ins->get_pitch_filter_envelope();
+ };
+
+ return 0;
+
+}
+
+const char * CPPlayer::get_voice_instrument_name(int p_voice) {
+
+
+
+ const char *name = NULL;
+
+
+
+ if (voice[p_voice].instrument_ptr!=NULL) name=voice[p_voice].instrument_ptr->get_name();
+
+
+
+ return name;
+
+}
+void CPPlayer::set_filters_enabled(bool p_enable){
+
+ control.filters=p_enable;
+}
+
+int CPPlayer::get_voice_sample_index(int p_voice) {
+
+ return voice[p_voice].sample_index;
+}
diff --git a/modules/chibi/cp_player_data_effects.cpp b/modules/chibi/cp_player_data_effects.cpp
new file mode 100644
index 0000000000..3a52a3b91b
--- /dev/null
+++ b/modules/chibi/cp_player_data_effects.cpp
@@ -0,0 +1,1232 @@
+/*************************************************************************/
+/* cp_player_data_effects.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+
+
+/**********************
+ complex effects
+***********************/
+#define RANDOM_MAX 2147483647
+
+static inline int32_t cp_random_generate(int32_t *seed) {
+ int32_t k;
+ int32_t s = (int32_t)(*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = (int32_t)s;
+ return (int32_t)(s & RANDOM_MAX);
+}
+
+
+
+void CPPlayer::do_vibrato(int p_track,bool fine) {
+
+ uint8_t q;
+ uint16_t temp=0;
+
+ if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].vibrato_position=0;
+
+ q=(control.channel[p_track].vibrato_position>>2)&0x1f;
+
+ switch (control.channel[p_track].vibrato_type) {
+ case 0: /* sine */
+ temp=vibrato_table[q];
+ break;
+ case 1: /* square wave */
+ temp=255;
+ break;
+ case 2: /* ramp down */
+ q<<=3;
+ if (control.channel[p_track].vibrato_position<0) q=255-q;
+ temp=q;
+ break;
+ case 3: /* random */
+ temp=cp_random_generate(&control.random_seed) %256;//getrandom(256);
+ break;
+ }
+
+ temp*=control.channel[p_track].vibrato_depth;
+
+ if (song->has_old_effects()) {
+
+ temp>>=7;
+ } else {
+
+ temp>>=8;
+ }
+
+ if (!fine) temp<<=2;
+
+ if (control.channel[p_track].vibrato_position>=0) {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period+temp;
+ } else {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period-temp;
+ }
+
+ if (!song->has_old_effects() || control.ticks_counter) control.channel[p_track].vibrato_position+=control.channel[p_track].vibrato_speed;
+}
+
+
+void CPPlayer::do_pitch_slide_down(int p_track,uint8_t inf) {
+
+ uint8_t hi,lo;
+
+ if (inf) control.channel[p_track].pitch_slide_info=inf;
+ else inf=control.channel[p_track].pitch_slide_info;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)lo<<2;
+ } else if (hi==0xe) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period+=lo;
+ } else {
+
+ if (control.ticks_counter) control.channel[p_track].aux_period+=(uint16_t)inf<<2;
+ }
+}
+
+void CPPlayer::do_pitch_slide_up(int p_track,uint8_t inf) {
+
+ uint8_t hi,lo;
+
+ if (inf) control.channel[p_track].pitch_slide_info=inf;
+ else inf=control.channel[p_track].pitch_slide_info;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)lo<<2;
+ } else if (hi==0xe) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_period-=lo;
+ } else {
+
+ if (control.ticks_counter) control.channel[p_track].aux_period-=(uint16_t)inf<<2;
+ }
+}
+
+void CPPlayer::do_pitch_slide_to_note(int p_track) {
+
+ if (control.ticks_counter) {
+ int dist;
+
+ /* We have to slide a->period towards a->wantedperiod, compute the
+ difference between those two values */
+ dist=control.channel[p_track].period-control.channel[p_track].slide_to_period;
+
+ /* if they are equal or if portamentospeed is too big... */
+ if ((!dist)||((control.channel[p_track].portamento_speed<<2)>cp_intabs(dist))) {
+ /* ... make tmpperiod equal tperiod */
+ control.channel[p_track].aux_period=control.channel[p_track].period=control.channel[p_track].slide_to_period;
+ } else {
+
+ if (dist>0) {
+
+ control.channel[p_track].aux_period-=control.channel[p_track].portamento_speed<<2;
+ control.channel[p_track].period-=control.channel[p_track].portamento_speed<<2; /* dist>0 slide up */
+ } else {
+ control.channel[p_track].aux_period+=control.channel[p_track].portamento_speed<<2;
+ control.channel[p_track].period+=control.channel[p_track].portamento_speed<<2; /* dist<0 slide down */
+ }
+ }
+
+ } else {
+
+ control.channel[p_track].aux_period=control.channel[p_track].period;
+ }
+}
+
+void CPPlayer::do_tremor(int p_track) {
+
+ uint8_t on,off,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) {
+ control.channel[p_track].tremor_info=inf;
+ } else {
+ inf= control.channel[p_track].tremor_info;
+ if (!inf) return;
+ }
+
+ //if (!control.ticks_counter) return;
+
+ on=(inf>>4);
+ off=(inf&0xf);
+
+ control.channel[p_track].tremor_position%=(on+off);
+ control.channel[p_track].volume=(control.channel[p_track].tremor_position<on)?control.channel[p_track].aux_volume:0;
+ control.channel[p_track].tremor_position++;
+}
+
+void CPPlayer::do_pan_slide(int p_track) {
+
+ uint8_t lo,hi,inf;
+ int16_t pan;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].channel_pan_slide_info=inf;
+ else inf=control.channel[p_track].channel_pan_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ pan=(control.channel[p_track].panning==PAN_SURROUND)?PAN_CENTER:control.channel[p_track].panning;
+
+ if (!hi)
+ pan+=lo<<2;
+ else
+ if (!lo) {
+ pan-=hi<<2;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) pan+=lo<<2;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) pan-=hi<<2;
+ }
+ //this sets both chan & voice paning
+ control.channel[p_track].panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
+ control.channel[p_track].channel_panning=control.channel[p_track].panning;
+}
+
+void CPPlayer::do_volume_slide(int p_track,int inf) {
+
+ uint8_t hi,lo;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+
+ if ((control.ticks_counter)) control.channel[p_track].aux_volume+=hi;
+
+ } else if (!hi) {
+
+ if ((control.ticks_counter)) control.channel[p_track].aux_volume-=lo;
+
+ } else if (lo==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_volume+=(hi?hi:0xf);
+ } else if (hi==0xf) {
+
+ if (!control.ticks_counter) control.channel[p_track].aux_volume-=(lo?lo:0xf);
+ } else return;
+
+ if (control.channel[p_track].aux_volume<0) {
+
+ control.channel[p_track].aux_volume=0;
+ } else if (control.channel[p_track].aux_volume>64) {
+
+ control.channel[p_track].aux_volume=64;
+ }
+}
+
+void CPPlayer::do_channel_volume_slide(int p_track) {
+
+ uint8_t lo, hi,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].channel_volume_slide_info=inf;
+ inf=control.channel[p_track].channel_volume_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!hi)
+ control.channel[p_track].channel_volume-=lo;
+ else
+ if (!lo) {
+ control.channel[p_track].channel_volume+=hi;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) control.channel[p_track].channel_volume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) control.channel[p_track].channel_volume+=hi;
+ }
+
+ if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
+ if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
+}
+
+void CPPlayer::do_tremolo(int p_track) {
+
+ uint8_t q;
+ int16_t temp=0;
+
+ if ((control.ticks_counter==0) && control.channel[p_track].row_has_note) control.channel[p_track].tremolo_position=0;
+
+ q=(control.channel[p_track].tremolo_position>>2)&0x1f;
+
+ switch (control.channel[p_track].tremolo_type) {
+ case 0: /* sine */
+ temp=vibrato_table[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (control.channel[p_track].tremolo_position<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random */
+ temp=cp_random_generate(&control.random_seed) % 256;//getrandom(256);
+ break;
+ }
+
+ temp*=control.channel[p_track].tremolo_depth;
+ temp>>=7;
+
+
+
+ if (control.channel[p_track].tremolo_position>=0) {
+
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume+temp;
+ if (control.channel[p_track].volume>64) control.channel[p_track].volume=64;
+ } else {
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume-temp;
+ if (control.channel[p_track].volume<0) control.channel[p_track].volume=0;
+ }
+
+ /*if (control.ticks_counter)*/ control.channel[p_track].tremolo_position+=control.channel[p_track].tremolo_speed;
+
+}
+
+void CPPlayer::do_arpegio(int p_track) {
+
+ uint8_t note,dat;
+ //note=control.channel[p_track].note;
+ note=0;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].arpegio_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].arpegio_info;
+
+ if (dat) {
+
+ switch (control.ticks_counter%3) {
+
+ case 1: {
+
+ note+=(dat>>4);
+
+ } break;
+ case 2: {
+
+ note+=(dat&0xf);
+ } break;
+ }
+
+ if (song->has_linear_slides()) {
+
+ control.channel[p_track].period=control.channel[p_track].aux_period-cp_intabs(get_period((uint16_t)46,0)-get_period((uint16_t)44,0))*note;
+ } else if (control.channel[p_track].sample_ptr) {
+
+ control.channel[p_track].period=get_period( (((uint16_t)control.channel[p_track].note)+note)<<1,CPSampleManager::get_singleton()->get_c5_freq( (control.channel[p_track].sample_ptr->get_sample_data())));
+ }
+
+ control.channel[p_track].has_own_period=true;
+ }
+
+
+}
+
+
+void CPPlayer::do_retrig(int p_track) {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) {
+
+ control.channel[p_track].retrig_volslide=inf>>4;
+ control.channel[p_track].retrig_speed=inf&0xf;
+ }
+
+ /* only retrigger if low nibble > 0 */
+ if ( control.channel[p_track].retrig_speed>0) {
+
+ if ( !control.channel[p_track].retrig_counter ) {
+ /* when retrig counter reaches 0, reset counter and restart the
+ sample */
+ if (control.channel[p_track].kick!=KICK_NOTE) control.channel[p_track].kick=KICK_NOTEOFF;
+ control.channel[p_track].retrig_counter=control.channel[p_track].retrig_speed;
+
+
+ if ((control.ticks_counter)/*||(pf->flags&UF_S3MSLIDES)*/) {
+ switch (control.channel[p_track].retrig_volslide) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ control.channel[p_track].aux_volume-=(1<<(control.channel[p_track].retrig_volslide-1));
+ break;
+ case 6:
+ control.channel[p_track].aux_volume=(2*control.channel[p_track].aux_volume)/3;
+ break;
+ case 7:
+ control.channel[p_track].aux_volume>>=1;
+ break;
+ case 9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ control.channel[p_track].aux_volume+=(1<<(control.channel[p_track].retrig_volslide-9));
+ break;
+ case 0xe:
+ control.channel[p_track].aux_volume=(3*control.channel[p_track].aux_volume)>>1;
+ break;
+ case 0xf:
+ control.channel[p_track].aux_volume=control.channel[p_track].aux_volume<<1;
+ break;
+ }
+ if (control.channel[p_track].aux_volume<0) control.channel[p_track].aux_volume=0;
+ else if (control.channel[p_track].aux_volume>64) control.channel[p_track].aux_volume=64;
+ }
+ }
+ control.channel[p_track].retrig_counter--; /* countdown */
+ }
+}
+
+void CPPlayer::do_global_volume_slide(int p_track) {
+
+ uint8_t lo,hi,inf;
+
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].global_volume_slide_info=inf;
+ inf=control.channel[p_track].global_volume_slide_info;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+ if (control.ticks_counter) control.global_volume+=hi;
+ } else
+ if (!hi) {
+ if (control.ticks_counter) control.global_volume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!control.ticks_counter) control.global_volume+=hi;
+ } else
+ if (hi==0xf) {
+ if (!control.ticks_counter) control.global_volume-=lo;
+ }
+
+ if (control.global_volume<0) control.global_volume=0;
+ if (control.global_volume>128) control.global_volume=128;
+}
+
+void CPPlayer::do_panbrello(int p_track) {
+
+ uint8_t q;
+ int32_t temp=0;
+
+ q=control.channel[p_track].panbrello_position;
+
+ switch (control.channel[p_track].panbrello_type) {
+ case 0: {/* sine */
+ temp=panbrello_table[q];
+ } break;
+ case 1: {/* square wave */
+ temp=(q<0x80)?64:0;
+ } break;
+ case 2: {/* ramp down */
+ q<<=3;
+ temp=q;
+ } break;
+ case 3: {/* random */
+ if (control.channel[p_track].panbrello_position>=control.channel[p_track].panbrello_speed) {
+ control.channel[p_track].panbrello_position=0;
+ temp=cp_random_generate(&control.random_seed)%256;//getrandom(256);
+ }
+ } break;
+ }
+
+
+
+ temp=temp*(int)control.channel[p_track].panbrello_depth/0xF;
+ temp<<=1;
+ if (control.channel[p_track].channel_panning!=PAN_SURROUND)
+ temp+=control.channel[p_track].channel_panning;
+
+ control.channel[p_track].panning=(temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
+ control.channel[p_track].panbrello_position+=control.channel[p_track].panbrello_speed;
+}
+
+/******************
+ S effect
+*******************/
+
+
+void CPPlayer::do_effect_S(int p_track) {
+
+ uint8_t inf,c,dat;
+
+ dat=control.channel[p_track].current_parameter;
+
+ inf=dat&0xf;
+ c=dat>>4;
+
+ if (!dat) {
+ c=control.channel[p_track].current_S_effect;
+ inf=control.channel[p_track].current_S_data;
+ } else {
+ control.channel[p_track].current_S_effect=c;
+ control.channel[p_track].current_S_data=inf;
+ }
+
+ switch (c) {
+ case 1: {/* S1x set glissando voice */
+ // this is unsupported in IT!
+
+ control.channel[p_track].chorus_send=inf*0xFF/0xF;
+
+ }break;
+ case 2: /* S2x set finetune */
+ //Also not supported!
+ break;
+ case 3: /* S3x set vibrato waveform */
+ if (inf<4) control.channel[p_track].vibrato_type=inf;
+ break;
+ case 4: /* S4x set tremolo waveform */
+ if (inf<4) control.channel[p_track].tremolo_type=inf;
+ break;
+ case 5: /* S5x panbrello */
+ if (inf<4) control.channel[p_track].panbrello_type=inf;
+ break;
+ case 6: {/* S6x delay x number of frames (patdly) */
+
+ if (control.ticks_counter) break;
+ if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
+
+ } break;
+ case 7: /* S7x instrument / NNA commands */
+
+ if (!song->has_instruments())
+ break;
+ switch(inf) {
+
+ case 0x3: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CUT;
+ } break;
+ case 0x4: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CONTINUE;
+ } break;
+ case 0x5: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_OFF;
+ } break;
+ case 0x6: {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_FADE;
+ } break;
+ case 0x7: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->volume_envelope_ctrl.active=false;
+ } break;
+ case 0x8: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->volume_envelope_ctrl.active=true;
+
+ } break;
+ case 0x9: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->panning_envelope_ctrl.active=false;
+
+ } break;
+ case 0xA: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->panning_envelope_ctrl.active=true;
+
+ } break;
+ case 0xB: {
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=false;
+
+ } break;
+ case 0xC: {
+
+ if (control.channel[p_track].slave_voice)
+ control.channel[p_track].slave_voice->pitch_envelope_ctrl.active=true;
+
+ } break;
+
+ } break;
+
+ break;
+ case 8: {/* S8x set panning position */
+
+// if (pf->panflag) {
+ if (inf<=8) inf<<=4;
+ else inf*=17;
+ control.channel[p_track].panning=control.channel[p_track].channel_panning=inf;
+// }
+ } break;
+
+ case 9: { /* S9x set surround sound */
+ //if (pf->panflag)
+ control.channel[p_track].panning=control.channel[p_track].channel_panning=PAN_SURROUND;
+ } break;
+ case 0xA:{ /* SAy set high order sample offset yxx00h */
+
+ if (control.channel[p_track].current_parameter) control.channel[p_track].hi_offset=(int32_t)inf<<16;
+ control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
+ } break;
+ case 0xB: { /* SBx pattern loop */
+ if (control.ticks_counter) break;
+
+ if (inf) { /* set reppos or repcnt ? */
+ /* set repcnt, so check if repcnt already is set, which means we
+ are already looping */
+ if (control.channel[p_track].pattern_loop_count>0)
+ control.channel[p_track].pattern_loop_count--; /* already looping, decrease counter */
+ else {
+ control.channel[p_track].pattern_loop_count=inf; /* not yet looping, so set repcnt */
+ }
+
+ if (control.channel[p_track].pattern_loop_count>0) { /* jump to reppos if repcnt>0 */
+
+ control.position=control.previous_position; // This will also anulate any Cxx or break..
+
+ control.position.current_row=control.channel[p_track].pattern_loop_position;
+ control.position.forbid_jump=true;
+ }
+
+ } else {
+
+
+ control.channel[p_track].pattern_loop_position=control.position.current_row-1;
+ }
+
+ } break;
+ case 0xC: { /* SCx notecut */
+
+ if (control.ticks_counter>=inf) {
+
+ control.channel[p_track].aux_volume=0;
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+ control.channel[p_track].note_end_flags|=END_NOTE_KILL;
+ }
+ } break;
+ case 0xD: {/* SDx notedelay */
+
+ if (!control.ticks_counter) {
+
+ control.channel[p_track].note_delay=inf;
+
+ } else if (control.channel[p_track].note_delay) {
+
+ control.channel[p_track].note_delay--;
+ }
+
+ } break;
+ case 0xF: {/* SEx patterndelay */
+
+ if (control.ticks_counter) break;
+ if (!control.pattern_delay_2) control.pattern_delay_1=inf+1; /* only once, when vbtick=0 */
+
+ } break;
+ }
+}
+
+
+
+
+
+
+
+
+/*********************
+ volume effects
+**********************/
+
+void CPPlayer::run_volume_column_effects(int p_track) {
+
+ uint8_t param=control.channel[p_track].current_volume_parameter;
+
+
+ switch ('A'+control.channel[p_track].current_volume_command) {
+
+ case 'A': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,param*0x10+0xF);
+
+ } break;
+ case 'B': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,0xF0+param);
+
+ } break;
+ case 'C': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+
+ do_volume_slide(p_track,param*0x10);
+ } break;
+ case 'D': {
+
+ if (param>0) control.channel[p_track].volcol_volume_slide=param;
+ else param=control.channel[p_track].volcol_volume_slide;
+ do_volume_slide(p_track,param);
+
+ } break;
+ case 'E': {
+
+ do_pitch_slide_down(p_track,param<<2);
+ } break;
+ case 'F': {
+
+ do_pitch_slide_up(p_track,param<<2);
+ } break;
+ case 'G': {
+
+ const uint8_t slide_table[]={0,1,4,8,16,32,64,96,128,255};
+ if (param) {
+
+ control.channel[p_track].portamento_speed=slide_table[param];
+ }
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+ //control.channel[p_track].kick=KICK_NOTE;
+ //control.channel[p_track].sample_start_index=0; // < am i stupid?
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ }
+ } break;
+ case 'H': {
+
+
+ if (!control.ticks_counter) {
+ if (param&0x0f) control.channel[p_track].vibrato_depth=param;
+ }
+ control.channel[p_track].doing_vibrato=true;
+ if (control.external_vibrato) break;
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ }
+}
+/*********************
+ table
+**********************/
+
+
+void CPPlayer::run_effects(int p_track) {
+
+ switch ('A'+control.channel[p_track].current_command) {
+
+ case 'A': {
+
+ if ((control.ticks_counter>0) || (control.pattern_delay_2>0)) break;
+
+ int new_speed;
+
+ new_speed=control.channel[p_track].current_parameter % 128;
+
+ if (new_speed>0) {
+ control.speed=new_speed;
+ control.ticks_counter=0;
+ }
+ } break;
+ case 'B': {
+
+ int next_order;
+
+ if (control.ticks_counter || control.position.forbid_jump) break;
+
+ control.position.current_row=0;
+
+ if (control.play_mode==PLAY_PATTERN) break;
+
+ next_order=get_song_next_order_idx(song, (int)control.channel[p_track].current_parameter-1);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+ control.position.force_next_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ }
+ } break;
+ case 'C': {
+
+ int next_order;
+
+ if (control.ticks_counter || control.position.forbid_jump) break;
+
+ control.position.current_row=control.channel[p_track].current_parameter;
+
+ if (control.play_mode==PLAY_PATTERN) {
+
+ if (control.position.current_row>=song->get_pattern(control.position.current_pattern)->get_length()) {
+
+ control.position.current_row=0;
+ }
+
+ break;
+ }
+
+ next_order=get_song_next_order_idx(song, (int)control.position.current_order);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+
+ if (control.position.current_row>=song->get_pattern(song->get_order(next_order))->get_length()) {
+
+ control.position.current_row=0;
+ }
+
+ control.position.force_next_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ }
+
+ } break;
+ case 'D': {
+
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ } break;
+ case 'E': {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+ do_pitch_slide_down(p_track,inf);
+
+ } break;
+ case 'F': {
+
+ uint8_t inf;
+
+ inf=control.channel[p_track].current_parameter;
+ do_pitch_slide_up(p_track,inf);
+
+ } break;
+ case 'G': {
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].portamento_speed=control.channel[p_track].current_parameter;
+ }
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+
+ control.channel[p_track].kick=KICK_NOTE;
+ control.channel[p_track].sample_start_index=0;
+
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ }
+
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'H': {
+
+ uint8_t dat;
+
+ control.channel[p_track].doing_vibrato=true;
+
+ dat=control.channel[p_track].current_parameter;
+
+ if (!control.ticks_counter) {
+ if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
+ }
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'I': {
+
+ do_tremor(p_track);
+ control.channel[p_track].has_own_volume=true;
+ } break;
+ case 'J': {
+
+ do_arpegio(p_track);
+ } break;
+ case 'K': {
+
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ control.channel[p_track].doing_vibrato=true;
+
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,false);
+ control.channel[p_track].has_own_period=true;
+ }
+
+ } break;
+ case 'L': {
+ uint8_t inf ;
+ //explicitslides=1;
+ inf=control.channel[p_track].current_parameter;
+
+ if (inf) control.channel[p_track].volume_slide_info=inf;
+ else inf=control.channel[p_track].volume_slide_info;
+
+ do_volume_slide(p_track,inf);
+
+ if (control.channel[p_track].period && (control.channel[p_track].old_note<=120)) {
+ if ( (!control.ticks_counter) && (control.channel[p_track].new_instrument) ){
+
+ control.channel[p_track].kick=KICK_NOTE;
+ control.channel[p_track].sample_start_index=0;
+
+ } else {
+
+ control.channel[p_track].kick=(control.channel[p_track].kick==KICK_NOTE)?KICK_ENVELOPE:KICK_NOTHING;
+ }
+
+ do_pitch_slide_to_note(p_track);
+ control.channel[p_track].has_own_period=true;
+ }
+ } break;
+ case 'M': {
+ control.channel[p_track].channel_volume=control.channel[p_track].current_parameter;
+ if (control.channel[p_track].channel_volume>64) control.channel[p_track].channel_volume=64;
+ else if (control.channel[p_track].channel_volume<0) control.channel[p_track].channel_volume=0;
+ } break;
+ case 'N': {
+
+ do_channel_volume_slide(p_track);
+ }
+ case 'O': {
+
+ if (!control.ticks_counter) {
+
+ if (control.channel[p_track].current_parameter) control.channel[p_track].lo_offset=(uint16_t)control.channel[p_track].current_parameter<<8;
+ control.channel[p_track].sample_start_index=control.channel[p_track].hi_offset|control.channel[p_track].lo_offset;
+
+ //if ((control.channel[p_track].sample_ptr!=NULL)&&(control.channel[p_track].sample_start_index>control.channel[p_track].sample_ptr->data.size)) {
+ //TODO, O effect
+ //a->start=a->s->flags&(SF_LOOP|SF_BIDI)?a->s->loopstart:a->s->length;
+ //}
+ }
+ } break;
+ case 'P': {
+
+ do_pan_slide(p_track);
+ } break;
+ case 'Q': {
+ do_retrig(p_track);
+
+ } break;
+ case 'R': {
+
+
+ uint8_t dat;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].tremolo_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].tremolo_info;
+
+ if (!control.ticks_counter && dat) {
+
+ if (dat&0x0f) control.channel[p_track].tremolo_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].tremolo_speed=(dat&0xf0)>>2;
+ }
+
+ do_tremolo(p_track);
+ control.channel[p_track].has_own_volume=true;
+
+ } break;
+ case 'S': {
+
+ do_effect_S(p_track);
+ } break;
+ case 'T': {
+ uint8_t dat;
+ int16_t temp=control.tempo;
+
+ if (control.pattern_delay_2) return;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].tempo_slide_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].tempo_slide_info;
+
+ if (dat>=0x20) {
+
+ if (control.ticks_counter) break;
+ control.tempo=dat;
+ } else {
+
+ if (!control.ticks_counter) break;
+
+ if (dat&0x10) {
+
+ temp+=(dat&0x0f);
+ } else {
+
+ temp-=dat;
+ }
+ control.tempo=(temp>255)?255:(temp<0x20?0x20:temp);
+ }
+
+ } break;
+ case 'U': {
+
+ uint8_t dat;
+
+ dat=control.channel[p_track].current_parameter;
+ control.channel[p_track].doing_vibrato=true;
+ if (!control.ticks_counter) {
+ if (dat&0x0f) control.channel[p_track].vibrato_depth=dat&0xf;
+ if (dat&0xf0) control.channel[p_track].vibrato_speed=(dat&0xf0)>>2;
+ }
+
+ if (control.external_vibrato) break;
+
+ if (control.channel[p_track].period) {
+
+ do_vibrato(p_track,true);
+ control.channel[p_track].has_own_period=true;
+ }
+ } break;
+ case 'V': {
+
+ control.global_volume=control.channel[p_track].current_parameter;
+ if (control.global_volume>128) control.global_volume=128;
+ } break;
+ case 'W': {
+ do_global_volume_slide(p_track);
+ } break;
+ case 'X': {
+ //sets both channel and current
+ control.channel[p_track].channel_panning=control.channel[p_track].current_parameter;
+ control.channel[p_track].panning=control.channel[p_track].current_parameter;
+ } break;
+ case 'Y': {
+
+ uint8_t dat;
+
+ if (control.channel[p_track].current_parameter) {
+
+ control.channel[p_track].panbrello_info=control.channel[p_track].current_parameter;
+ }
+
+ dat=control.channel[p_track].panbrello_info;
+
+ if (!control.ticks_counter) {
+
+ if (dat&0x0f) control.channel[p_track].panbrello_depth=(dat&0xf);
+ if (dat&0xf0) control.channel[p_track].panbrello_speed=(dat&0xf0)>>4;
+ }
+
+ //if (pf->panflag)
+ if (control.channel[p_track].panning!=PAN_SURROUND)do_panbrello(p_track);
+
+ } break;
+ case 'Z': {
+ //I DO! cuttoff!
+ uint16_t dat=control.channel[p_track].current_parameter;
+
+ if (dat<0x80) {
+
+ control.channel[p_track].filter.it_cutoff=dat*2;
+ if (control.channel[p_track].filter.it_cutoff>0x80)
+ control.channel[p_track].filter.it_cutoff++;
+ } else if (dat<0x90) {
+
+ control.channel[p_track].filter.it_reso=(dat-0x80)*0x10;
+ } else {
+
+ control.channel[p_track].reverb_send=(dat-0x90)*255/0x6F;
+ }
+
+ } break;
+
+ }
+
+}
+
+void CPPlayer::pre_process_effects() {
+
+// MP_VOICE *aout;
+ int i;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ //a=&pf->control[mp_channel];
+
+ // if ((aout=a->slave)) {
+ // a->fadevol=aout->fadevol;
+ // a->period=aout->period;
+ // if (a->kick==KICK_KEYOFF) a->keyoff=aout->keyoff;
+ //}
+
+ //if (!a->row) continue;
+ //UniSetRow(a->row);
+ control.channel[i].has_own_period=false;
+ control.channel[i].has_own_volume=false;
+ control.channel[i].doing_vibrato=false;
+ //explicitslides=0;
+ //pt_playeffects();
+ if (control.ticks_counter<control.speed) {
+
+ run_effects(i);
+ run_volume_column_effects(i);
+ }
+
+ /* continue volume slide if necessary for XM and IT */
+ //if (pf->flags&UF_BGSLIDES) {
+ // if (!explicitslides && a->sliding)
+ // DoS3MVolSlide(0);
+ // else if (a->tmpvolume) a->sliding=explicitslides;
+ //}
+
+ if (!control.channel[i].has_own_period) control.channel[i].period=control.channel[i].aux_period;
+ if (!control.channel[i].has_own_volume) control.channel[i].volume=control.channel[i].aux_volume;
+
+ if ((control.channel[i].sample_ptr!=NULL) && !(song->has_instruments() && (control.channel[i].instrument_ptr==NULL))) {
+
+ if (song->has_instruments()) {
+
+ control.channel[i].output_volume=
+ (control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume()*control.channel[i].instrument_ptr->get_volume_global_amount())/2048;
+ control.channel[i].output_volume=control.channel[i].output_volume*control.channel[i].random_volume_variation/100;
+
+ } else {
+
+ control.channel[i].output_volume=
+ (control.channel[i].volume*control.channel[i].sample_ptr->get_global_volume())>>4;
+
+ }
+
+ if (control.channel[i].output_volume>256) {
+
+ control.channel[i].output_volume=256;
+
+ } else if (control.channel[i].output_volume<0) {
+
+ control.channel[i].output_volume=0;
+ }
+
+
+ }
+ }
+
+}
diff --git a/modules/chibi/cp_player_data_envelopes.cpp b/modules/chibi/cp_player_data_envelopes.cpp
new file mode 100644
index 0000000000..96af42d19f
--- /dev/null
+++ b/modules/chibi/cp_player_data_envelopes.cpp
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_player_data_envelopes.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+
+void CPPlayer::Voice_Control::start_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl,Envelope_Control *p_from_env) {
+
+
+ if (p_from_env && p_envelope->is_carry_enabled() && !p_from_env->terminated) {
+
+
+ *p_envelope_ctrl=*p_from_env;
+ } else {
+ p_envelope_ctrl->pos_index=0;
+ p_envelope_ctrl->status=1;
+ p_envelope_ctrl->sustain_looping=p_envelope->is_sustain_loop_enabled();
+ p_envelope_ctrl->looping=p_envelope->is_loop_enabled();
+ p_envelope_ctrl->terminated=false;
+ p_envelope_ctrl->kill=false;
+ p_envelope_ctrl->value=p_envelope->get_height_at_pos(p_envelope_ctrl->pos_index);
+ }
+}
+
+bool CPPlayer::Voice_Control::process_envelope(CPEnvelope *p_envelope,Envelope_Control *p_envelope_ctrl) {
+
+ if (!p_envelope_ctrl->active)
+ return false;
+
+ if (note_end_flags&END_NOTE_OFF) p_envelope_ctrl->sustain_looping=false;
+
+ p_envelope_ctrl->value=p_envelope->get_height_at_pos(p_envelope_ctrl->pos_index);
+ if (p_envelope_ctrl->value==CPEnvelope::NO_POINT)
+ return false;
+
+
+ p_envelope_ctrl->pos_index++;
+
+ if (p_envelope_ctrl->sustain_looping) {
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_sustain_loop_end()).tick_offset) {
+
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_sustain_loop_begin()).tick_offset;
+ }
+
+ } else if (p_envelope_ctrl->looping) {
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_loop_end()).tick_offset) {
+
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_loop_begin()).tick_offset;
+ }
+
+ }
+
+ if (p_envelope_ctrl->pos_index>p_envelope->get_node(p_envelope->get_node_count()-1).tick_offset) {
+
+ p_envelope_ctrl->terminated=true;
+ p_envelope_ctrl->pos_index=p_envelope->get_node(p_envelope->get_node_count()-1).tick_offset;
+ if (p_envelope->get_node(p_envelope->get_node_count()-1).value==0) p_envelope_ctrl->kill=true;
+ }
+
+ return true;
+}
diff --git a/modules/chibi/cp_player_data_events.cpp b/modules/chibi/cp_player_data_events.cpp
new file mode 100644
index 0000000000..fb5090461b
--- /dev/null
+++ b/modules/chibi/cp_player_data_events.cpp
@@ -0,0 +1,679 @@
+/*************************************************************************/
+/* cp_player_data_events.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include "cp_sample_manager.h"
+#include "stdio.h"
+/*
+ setup_voices():
+
+This will go throught all the REAL channels, if it finds a channel
+that needs to be restarted or assigned a new VIRTUAL channel, then it
+will just find one and do it.
+
+*/
+
+
+#define C5FREQ 261.6255653006
+static const int32_t C5FREQ_MIXER = ((int32_t)(C5FREQ*(float)(1<<CPMixer::FREQUENCY_BITS)));
+
+
+void CPPlayer::setup_voices() {
+
+ int i,voice_index;
+
+
+
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ voice_index=-1;
+
+ if (control.channel[i].note_delay) continue;
+
+
+ // check if we need a new empty voice
+ if (control.channel[i].kick==KICK_NOTE) {
+
+ /* if no channel was cut above, find an empty or quiet channel
+ here */
+ if ( song->has_instruments() && !control.force_no_nna) {
+
+ if (control.channel[i].slave_voice==NULL) { // no slave??
+
+ int newchn;
+ if ((newchn=find_empty_voice())!=-1) {
+
+ control.channel[i].slave_voice_index=newchn;
+ control.channel[i].slave_voice=&voice[newchn];
+ }
+ }
+
+ } else {
+ if (i<control.max_voices) {
+
+ control.channel[i].slave_voice_index=i;
+ control.channel[i].slave_voice=&voice[i];
+ } else {
+ //This is a _DIRTY_ hack, but i cant think a better way.
+ control.channel[i].slave_voice_index=control.max_voices-1;
+ control.channel[i].slave_voice=&voice[control.max_voices-1];
+ }
+
+ }
+
+
+ /* assign parts of MP_VOICE only done for a KICK_NOTE */
+ if ( ( control.channel[i].slave_voice!=NULL ) ) {
+
+ voice_index=control.channel[i].slave_voice_index;
+ Voice_Control &v=voice[voice_index];
+
+ if (v.has_master_channel && (v.master_channel!=NULL) ) {
+ // If this voice already has a master channel, make sure to remove the reference to it.
+ v.master_channel->slave_voice=NULL;
+
+ }
+ //notify the voice that the current channel is the master
+ v.master_channel=&control.channel[i];
+ //set the voice as slave of the current channel
+ control.channel[i].slave_voice=&v;
+ //master channel index of the voice
+ v.master_channel_index=i;
+ v.has_master_channel=true;
+ }
+
+ } else {
+ // nope..
+ // so if we DO have a slave voice then use it.
+ if ( control.channel[i].slave_voice!=NULL ) {
+
+ voice_index=control.channel[i].slave_voice_index;
+ }
+ }
+ //assuming this channel has a slave voice..
+ if (voice_index>=0) {
+
+ // IMPROVE: Code a method for this:
+ voice[voice_index].update_info_from_master_channel();
+ }
+
+ control.channel[i].kick=KICK_NOTHING;
+ }
+}
+void CPPlayer::Voice_Control::reset() {
+
+ cp_memzero(this,sizeof(*this));
+
+ instrument_ptr=NULL;
+ sample_ptr=NULL;
+ has_master_channel=false;
+ instrument_index=-1;
+ reverb_send=0;
+ chorus_send=0;
+ filter.it_cutoff=255;
+ filter.it_reso=0;
+ display_volume=0;
+
+
+}
+
+void CPPlayer::Channel_Control::reset() {
+
+ int prev_gv =channel_global_volume;
+ cp_memzero(this,sizeof(*this));
+
+ slave_voice=NULL;
+ slave_voice_index=255;
+
+ mute=false;
+ old_note=255;
+ real_note=255;
+ instrument_index=255;
+ filter.it_cutoff=255;
+ filter.it_reso=0;
+ reverb_send=0;
+ chorus_send=0;
+ reserved=false;
+ carry.maybe=false;
+ last_event_usecs=-1;
+ channel_global_volume=prev_gv;
+}
+
+void CPPlayer::Voice_Control::update_info_from_master_channel() {
+
+ instrument_ptr=master_channel->instrument_ptr;
+ sample_ptr=master_channel->sample_ptr;
+
+ instrument_index=master_channel->instrument_index;
+ sample_index=master_channel->sample_index;
+
+ note=master_channel->note;
+ output_volume=master_channel->output_volume;
+
+ channel_volume=master_channel->channel_volume;
+
+ panning=master_channel->panning;
+
+ kick=master_channel->kick;
+ note_end_flags=master_channel->note_end_flags;
+ period=master_channel->period;
+
+ volume_envelope_ctrl.active=master_channel->volume_envelope_on;
+ panning_envelope_ctrl.active=master_channel->panning_envelope_on;
+ pitch_envelope_ctrl.active=master_channel->pitch_envelope_on;
+
+
+ NNA_type=master_channel->NNA_type;
+ reverb_send=master_channel->reverb_send;
+ chorus_send=master_channel->chorus_send;
+
+// last_note_type=master_channel->last_note_type;
+
+ sample_start_index=master_channel->sample_start_index;
+ filter=master_channel->filter;
+
+}
+
+
+void CPPlayer::update_mixer() {
+
+ int tmp_mixer_period;
+ int32_t tmp_vibrato_value,tmp_vibrato_depth,tmp_volenv_value;
+ uint64_t tmpvol; // 64bits should be the only way to avoid getting notes raped out
+ int i;
+
+
+ control.voices_used=0;
+
+ for (i=0;i<control.max_voices;i++) {
+
+
+ int filter_env=-1;
+ Voice_Control &v=voice[i];
+
+ if ( !((v.kick==KICK_NOTE)||(v.kick==KICK_NOTEOFF)) && !is_voice_active(i))
+ continue;
+
+ //if voice doesnt have a sample set or size is 0.. forget it
+ if ( v.sample_ptr==NULL) continue;
+
+
+ //TODO set limits somewhere else
+
+ if (v.period<40) {
+
+ v.period=40;
+
+ } else if (v.period>50000) {
+
+ v.period=50000;
+ }
+
+
+ if ((v.kick==KICK_NOTE)||(v.kick==KICK_NOTEOFF)) {
+
+ int real_start_index;
+
+ if (v.sample_start_index==-1) {
+
+ real_start_index=0;
+
+ } else {
+
+ real_start_index=v.sample_start_index;
+ }
+
+ mixer->setup_voice(i,v.sample_ptr->get_sample_data(),real_start_index);
+ v.fadeout_volume=1024; //IT Docs it is 1024 internally
+ v.auto_vibrato_sweep_pos=0;
+
+
+
+ }
+
+
+ /* Start Envelopes */
+ if ( song->has_instruments() && ((v.kick==KICK_NOTE)||(v.kick==KICK_ENVELOPE))) {
+
+// Voice_Control *carry=0;
+
+
+ if (v.has_master_channel && v.master_channel->carry.maybe) {
+
+ v.start_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl,&v.master_channel->carry.vol);
+ v.start_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl,&v.master_channel->carry.pan);
+ v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl,&v.master_channel->carry.pitch);
+
+ } else {
+
+ v.start_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl,NULL);
+ v.start_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl,NULL);
+ v.start_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl,NULL);
+
+ }
+
+
+ }
+
+ v.kick=KICK_NOTHING;
+
+ if (song->has_instruments()) {
+
+ if (!v.process_envelope(v.instrument_ptr->get_volume_envelope(),&v.volume_envelope_ctrl))
+ v.volume_envelope_ctrl.value=64;
+
+ if (!v.process_envelope(v.instrument_ptr->get_pan_envelope(),&v.panning_envelope_ctrl))
+ v.panning_envelope_ctrl.value=0;
+
+ if (!v.process_envelope(v.instrument_ptr->get_pitch_filter_envelope(),&v.pitch_envelope_ctrl))
+ v.pitch_envelope_ctrl.value=0;
+
+
+ if (v.volume_envelope_ctrl.terminated) {
+
+ if (v.volume_envelope_ctrl.kill) {
+
+ v.fadeout_volume=0;
+ } else {
+
+ v.note_end_flags|=END_NOTE_FADE;
+ }
+ }
+
+ }
+
+ if (song->has_instruments()) {
+
+ tmp_volenv_value=v.volume_envelope_ctrl.value;
+ } else {
+
+ tmp_volenv_value=64;
+
+ }
+
+ /*printf("fadeout %i\n",(int)v.fadeout_volume);
+ printf("channel %i\n",(int)v.channel_volume);
+ printf("output %i\n",(int)v.output_volume);
+ printf("env %i\n",(int)tmp_volenv_value);
+ printf("cgb %i\n",(int)v.master_channel->channel_global_volume);
+*/
+
+
+ tmpvol=(uint64_t)v.fadeout_volume; /* max 1024 - 10 bits */
+ tmpvol*=(uint64_t)v.channel_volume; /* * max 64 - 6 bits */
+ tmpvol*=(uint64_t)v.output_volume; /* * max 256 - 8 bits */
+ tmpvol*=(uint64_t)tmp_volenv_value; /* max 64 - 6 bits*/
+ tmpvol*=(uint64_t)v.master_channel->channel_global_volume;
+ v.display_volume=tmpvol>>22; //volume used for display purposes , 0 -- 256
+
+ tmpvol*=(uint64_t)song->get_mixing_volume(); /* max 128 - 7 bits */
+ tmpvol*=(uint64_t)control.global_volume; /* max 128 - 7 bits*/
+ /* total 10+6+8+6+7+7=44 bits */
+
+ tmpvol>>=43; /* Move back to 8 bits range , 44-19+8=43*/
+
+ if (tmpvol>CP_VOL_MAX)
+ tmpvol=CP_VOL_MAX;
+
+ //printf("volume check - fade %i, channel %i, output %i, env %i, mix %i, global %i -- final %i\n",v.fadeout_volume, v.channel_volume,v.output_volume,tmp_volenv_value, song->get_mixing_volume(),control.global_volume,tmpvol);
+
+ v.total_volume=tmpvol;
+
+
+ if ((v.master_channel!=NULL) && song->is_channel_mute( v.master_channel_index ) && !v.master_channel->reserved) {
+
+ mixer->set_voice_volume(i,0);
+ } else {
+ mixer->set_voice_volume(i,tmpvol);
+ if (v.fadeout_volume>0) control.voices_used++;
+ }
+
+
+ if (!song->is_stereo()) {
+
+ mixer->set_voice_panning(i,PAN_CENTER);
+
+ } else if (v.panning==PAN_SURROUND) {
+
+ mixer->set_voice_panning(i,PAN_SURROUND);
+ } else if (song->has_instruments()) {
+
+ int newpan,real_modifier;
+
+
+ real_modifier=(v.panning_envelope_ctrl.value*(PAN_CENTER-cp_intabs(v.panning-PAN_CENTER)))/32;
+
+ newpan=v.panning+real_modifier;
+
+ newpan=(newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT)?PAN_RIGHT:newpan;
+ //printf("panenv val: %i, finalpan val %i\n",v.panning_envelope_ctrl.value,newpan);
+
+ mixer->set_voice_panning(i,newpan);
+ } else {
+ mixer->set_voice_panning(i,v.panning);
+ }
+
+
+
+ /* VIBRATO */
+
+ if ( (v.period>0) && (v.sample_ptr->get_vibrato_depth()>0) ) {
+
+ switch (v.sample_ptr->get_vibrato_type()) {
+ case CPSample::VIBRATO_SINE:
+ tmp_vibrato_value=auto_vibrato_table[v.auto_vibrato_pos&127];
+ if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value=-tmp_vibrato_value;
+ break;
+ case CPSample::VIBRATO_SQUARE:
+ tmp_vibrato_value=64;
+ if (v.auto_vibrato_pos & 0x80) tmp_vibrato_value=-tmp_vibrato_value;
+ break;
+ case CPSample::VIBRATO_SAW:
+ tmp_vibrato_value=63-(((v.auto_vibrato_pos+128)&255)>>1);
+ break;
+ default:
+ tmp_vibrato_value=(((v.auto_vibrato_pos+128)&255)>>1)-64;
+ break;
+ }
+ } else {
+
+ tmp_vibrato_value=0;
+ }
+
+ if ((v.auto_vibrato_sweep_pos>>8)<v.sample_ptr->get_vibrato_depth()) {
+
+ v.auto_vibrato_sweep_pos+=v.sample_ptr->get_vibrato_speed(); //FIXME - speed? i think so
+ tmp_vibrato_depth=v.auto_vibrato_sweep_pos;
+
+ } else {
+
+ tmp_vibrato_depth=v.sample_ptr->get_vibrato_depth()<<8;
+ }
+
+ tmp_vibrato_value=(tmp_vibrato_value*tmp_vibrato_depth)>>16;
+ if (song->has_linear_slides())
+ tmp_vibrato_value>>=1;
+ v.period-=tmp_vibrato_value;
+
+
+ /* update vibrato position */
+ v.auto_vibrato_pos=(v.auto_vibrato_pos+v.sample_ptr->get_vibrato_rate())&0xff;
+
+
+ /* process pitch envelope */
+ tmp_mixer_period=v.period;
+
+ if (v.pitch_envelope_ctrl.active) {
+
+ long aux_pitch_diff;
+ int pe_value=v.pitch_envelope_ctrl.value;
+
+ if (!v.instrument_ptr->is_pitch_use_as_filter()) {
+
+
+ if (((uint16_t)v.note<<1)+pe_value<=0)
+ pe_value=-(v.note<<1);
+
+ int smp_c5=CPSampleManager::get_singleton()->get_c5_freq( v.sample_ptr->get_sample_data());
+
+ int base=get_period(((uint16_t)v.note<<1),smp_c5);
+ int env=get_period(((uint16_t)v.note<<1)+pe_value,smp_c5);
+ /*
+ int env_next=(pe_value<0)?get_period(((uint16_t)(v.note-1)<<1)+pe_value,smp_c5):get_period(((uint16_t)(v.note+1)<<1)+pe_value,smp_c5);
+
+ env=env+(abs(v.pitch_envelope_ctrl.value)&((1<<CPEnvelope::FX_HEIGHT_BITS)-1))*(env_next-env)/(1<<CPEnvelope::FX_HEIGHT_BITS);
+
+ printf("env %i\n",env);
+ */
+ aux_pitch_diff=env-base;
+
+
+ if ( ((int)tmp_mixer_period-aux_pitch_diff)<0 ) aux_pitch_diff=0;
+
+ tmp_mixer_period+=aux_pitch_diff;
+
+ } else {
+
+ filter_env=pe_value+32; //max 64
+// printf("pitch envelope at %i",filter_env);
+
+ }
+ }
+
+ if (v.fadeout_volume==0 || (v.note_end_flags & END_NOTE_KILL)) { /* check for a dead note (fadevol=0) */
+
+ mixer->stop_voice(i);
+
+ } else {
+
+
+ int32_t freq=get_frequency(tmp_mixer_period);
+ int32_t tracker_c5=get_frequency(get_period(60<<1,CPSampleManager::get_singleton()->get_c5_freq( v.sample_ptr->get_sample_data())));
+
+ freq=(int32_t)((uint64_t)freq*(uint64_t)C5FREQ_MIXER/(uint64_t)tracker_c5); //numbers may become very high
+ mixer->set_voice_frequency(i,freq);
+
+ /* if keyfade, start substracting fadeoutspeed from fadevol: */
+ if ((song->has_instruments())&&(v.note_end_flags & END_NOTE_FADE)) {
+
+ if (v.fadeout_volume>=(v.instrument_ptr->get_volume_fadeout())) {
+
+ v.fadeout_volume-=(v.instrument_ptr->get_volume_fadeout());
+ } else {
+
+ v.fadeout_volume=0;
+ }
+ }
+
+ /*FILTARSSSSSSSS*/
+
+
+
+ v.filter.envelope_cutoff=filter_env;
+ v.filter.process();
+
+ if ((v.filter.final_cutoff<0xFF) && (control.filters)) {
+
+ //int final_cutoff;
+ //uint8_t final_reso;
+
+ //v.filter.set_filter_parameters( &final_cutoff, &final_reso );
+
+ mixer->set_voice_filter(i,true,v.filter.final_cutoff,v.filter.it_reso);
+ } else {
+
+
+ mixer->set_voice_filter(i,false,0,0);
+ }
+
+ /* RAIVERV */
+
+ mixer->set_voice_reverb_send(i,v.reverb_send);
+
+ /* CHAURUZ */
+
+ mixer->set_voice_chorus_send(i,v.chorus_send);
+
+ }
+ }
+
+
+ switch(song->get_reverb_mode()) {
+
+ case CPSong::REVERB_MODE_ROOM: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_ROOM );
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_SMALL: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_SMALL );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_MEDIUM: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_MEDIUM );
+
+ } break;
+ case CPSong::REVERB_MODE_STUDIO_LARGE: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_STUDIO_LARGE );
+
+ } break;
+ case CPSong::REVERB_MODE_HALL: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_HALL );
+
+ } break;
+ case CPSong::REVERB_MODE_SPACE_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_SPACE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_ECHO );
+
+ } break;
+ case CPSong::REVERB_MODE_DELAY: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_DELAY );
+
+ } break;
+ case CPSong::REVERB_MODE_HALF_ECHO: {
+
+ mixer->set_reverb_mode( CPMixer::REVERB_MODE_HALF_ECHO );
+
+ } break;
+
+ }
+
+ mixer->set_chorus_params(song->get_chorus_delay_ms(),song->get_chorus_separation_ms(),song->get_chorus_depth_ms10(),song->get_chorus_speed_hz10() );
+
+
+}
+
+
+
+
+
+
+
+
+void CPPlayer::handle_tick() {
+
+ int i;
+
+
+ if ( mixer==NULL ) return;
+ if ( song==NULL ) return;
+
+
+ /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */
+
+ if (control.ticks_counter>=control.speed) { // time to process... ***THE ROW***!
+
+ /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is
+ the command memory. */
+
+// if (control.pattern_delay_1) {
+
+// control.pattern_delay_2=control.pattern_delay_1;
+// control.pattern_delay_1=0;
+// }
+// if (control.pattern_delay_2) {
+// patterndelay active
+// if (--control.pattern_delay_2)
+// so turn back pf->patpos by 1
+// if (pf->patpos) pf->patpos--;
+// }
+
+ if (control.play_mode!=PLAY_NOTHING) {
+
+ control.ticks_counter=0;
+
+
+ if (control.position.force_next_order>=0) {
+
+ control.position.current_order=control.position.force_next_order;
+ }
+
+ control.position.force_next_order=-1;
+
+ control.previous_position=control.position; // for those special cases...
+ control.position.forbid_jump=false;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ process_note(i,song->get_pattern(control.position.current_pattern)->get_note(i,control.position.current_row));
+ }
+
+ control.position.current_row++;
+
+ if ( control.position.current_row>=song->get_pattern(control.position.current_pattern)->get_length() ) {
+
+ if (control.play_mode==PLAY_SONG) {
+
+ int next_order;
+
+ next_order=get_song_next_order_idx(song,control.position.current_order);
+
+ if (next_order!=-1) {
+ // Do we have a "next order?"
+ control.position.current_pattern=song->get_order(next_order);
+ if (next_order<=control.position.current_order)
+ control.reached_end=true;
+ control.position.current_order=next_order;
+
+ } else {
+ // no, probably the user deleted the orderlist.
+ control.play_mode=PLAY_NOTHING;
+ reset();
+ control.reached_end=true;
+ }
+ }
+ control.position.current_row=0;
+ }
+
+ }
+
+
+ }
+
+
+
+ pre_process_effects();
+ process_NNAs();
+ setup_voices();
+
+ /* now set up the actual hardware channel playback information */
+ update_mixer();
+
+ control.ticks_counter++;
+}
diff --git a/modules/chibi/cp_player_data_filter.cpp b/modules/chibi/cp_player_data_filter.cpp
new file mode 100644
index 0000000000..30db807eed
--- /dev/null
+++ b/modules/chibi/cp_player_data_filter.cpp
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* cp_player_data_filter.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+
+#include "cp_player_data.h"
+
+static float filter_cutoff[256] = {
+ 130, 132, 134, 136, 138, 140, 142, 144,
+ 146, 148, 151, 153, 155, 157, 160, 162,
+ 164, 167, 169, 172, 174, 177, 179, 182,
+ 184, 187, 190, 193, 195, 198, 201, 204,
+ 207, 210, 213, 216, 220, 223, 226, 229,
+ 233, 236, 239, 243, 246, 250, 254, 257,
+ 261, 265, 269, 273, 277, 281, 285, 289,
+ 293, 297, 302, 306, 311, 315, 320, 324,
+ 329, 334, 339, 344, 349, 354, 359, 364,
+ 369, 375, 380, 386, 391, 397, 403, 409,
+ 415, 421, 427, 433, 440, 446, 452, 459,
+ 466, 472, 479, 486, 493, 501, 508, 515,
+ 523, 530, 538, 546, 554, 562, 570, 578,
+ 587, 595, 604, 613, 622, 631, 640, 649,
+ 659, 668, 678, 688, 698, 708, 718, 729,
+ 739, 750, 761, 772, 783, 795, 806, 818,
+ 830, 842, 854, 867, 880, 892, 905, 918,
+ 932, 945, 959, 973, 987, 1002, 1016, 1031,
+ 1046, 1061, 1077, 1092, 1108, 1124, 1141, 1157,
+ 1174, 1191, 1209, 1226, 1244, 1262, 1280, 1299,
+ 1318, 1337, 1357, 1376, 1396, 1417, 1437, 1458,
+ 1479, 1501, 1523, 1545, 1567, 1590, 1613, 1637,
+ 1661, 1685, 1709, 1734, 1760, 1785, 1811, 1837,
+ 1864, 1891, 1919, 1947, 1975, 2004, 2033, 2062,
+ 2093, 2123, 2154, 2185, 2217, 2249, 2282, 2315,
+ 2349, 2383, 2418, 2453, 2489, 2525, 2561, 2599,
+ 2637, 2675, 2714, 2753, 2793, 2834, 2875, 2917,
+ 2959, 3003, 3046, 3091, 3135, 3181, 3227, 3274,
+ 3322, 3370, 3419, 3469, 3520, 3571, 3623, 3675,
+ 3729, 3783, 3838, 3894, 3951, 4008, 4066, 4125,
+ 4186, 4246, 4308, 4371, 4434, 4499, 4564, 4631,
+ 4698, 4766, 4836, 4906, 4978, 5050, 5123, 5198
+};
+
+
+void CPPlayer::Filter_Control::process() {
+
+
+ final_cutoff=it_cutoff;
+ if (envelope_cutoff>=0) {
+
+ envelope_cutoff=envelope_cutoff*255/64;
+ final_cutoff=final_cutoff*envelope_cutoff/255;
+ if (final_cutoff>=0xFF) final_cutoff=0xFE;
+
+ }
+
+}
+
+void CPPlayer::Filter_Control::set_filter_parameters(int *p_cutoff,uint8_t *p_reso) {
+
+
+
+ *p_cutoff=filter_cutoff[final_cutoff];
+ *p_reso=it_reso;
+}
diff --git a/modules/chibi/cp_player_data_nna.cpp b/modules/chibi/cp_player_data_nna.cpp
new file mode 100644
index 0000000000..844f043694
--- /dev/null
+++ b/modules/chibi/cp_player_data_nna.cpp
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* cp_player_data_nna.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+
+void CPPlayer::process_NNAs() {
+
+ int i;
+
+ if (!song->has_instruments()) return;
+
+ for (i=0;i<CPPattern::WIDTH;i++) {
+
+ Channel_Control *aux_chn_ctrl = &control.channel[i];
+
+ if (aux_chn_ctrl->kick==KICK_NOTE) {
+
+ bool k=false;
+
+ if (aux_chn_ctrl->slave_voice!=NULL) {
+
+ Voice_Control *aux_voc_ctrl;
+
+ aux_voc_ctrl=aux_chn_ctrl->slave_voice;
+
+ if (aux_chn_ctrl->instrument_index==aux_chn_ctrl->slave_voice->instrument_index) { //maybe carry
+
+ aux_chn_ctrl->carry.pan=aux_chn_ctrl->slave_voice->panning_envelope_ctrl;
+ aux_chn_ctrl->carry.vol=aux_chn_ctrl->slave_voice->volume_envelope_ctrl;
+ aux_chn_ctrl->carry.pitch=aux_chn_ctrl->slave_voice->pitch_envelope_ctrl;
+ aux_chn_ctrl->carry.maybe=true;
+ } else
+ aux_chn_ctrl->carry.maybe=false;
+
+ if (aux_voc_ctrl->NNA_type != CPInstrument::NNA_NOTE_CUT) {
+ /* Make sure the old MP_VOICE channel knows it has no
+ master now ! */
+
+
+
+ aux_chn_ctrl->slave_voice=NULL;
+ /* assume the channel is taken by NNA */
+ aux_voc_ctrl->has_master_channel=false;
+
+ switch (aux_voc_ctrl->NNA_type) {
+ case CPInstrument::NNA_NOTE_CONTINUE: {
+
+ } break;
+ case CPInstrument::NNA_NOTE_OFF: {
+
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_OFF;
+
+ if (!aux_voc_ctrl->volume_envelope_ctrl.active || aux_voc_ctrl->instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ }
+ } break;
+ case CPInstrument::NNA_NOTE_FADE: {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ } break;
+ }
+ }
+ }
+
+ if (aux_chn_ctrl->duplicate_check_type!=CPInstrument::DCT_DISABLED) {
+ int i;
+
+ for (i=0;i<control.max_voices;i++) {
+ if (!mixer->is_voice_active(i)||
+ (voice[i].master_channel!=aux_chn_ctrl) ||
+ (aux_chn_ctrl->instrument_index!=voice[i].instrument_index))
+ continue;
+
+ Voice_Control *aux_voc_ctrl;
+
+ aux_voc_ctrl=&voice[i];
+
+ k=false;
+ switch (aux_chn_ctrl->duplicate_check_type) {
+ case CPInstrument::DCT_NOTE:
+ if (aux_chn_ctrl->note==aux_voc_ctrl->note)
+ k=true;
+ break;
+ case CPInstrument::DCT_SAMPLE:
+ if (aux_chn_ctrl->sample_ptr==aux_voc_ctrl->sample_ptr)
+ k=true;
+ break;
+ case CPInstrument::DCT_INSTRUMENT:
+ k=true;
+ break;
+ }
+ if (k) {
+ switch (aux_chn_ctrl->duplicate_check_action) {
+ case CPInstrument::DCA_NOTE_CUT: {
+ aux_voc_ctrl->fadeout_volume=0;
+ } break;
+ case CPInstrument::DCA_NOTE_OFF: {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_OFF;
+
+ if (!aux_voc_ctrl->volume_envelope_ctrl.active || aux_chn_ctrl->instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ }
+
+ } break;
+ case CPInstrument::DCA_NOTE_FADE: {
+ aux_voc_ctrl->note_end_flags|=END_NOTE_FADE;
+ } break;
+ }
+ }
+ }
+
+ }
+ } /* if (aux_chn_ctrl->kick==KICK_NOTE) */
+ }
+}
diff --git a/modules/chibi/cp_player_data_notes.cpp b/modules/chibi/cp_player_data_notes.cpp
new file mode 100644
index 0000000000..621be019e1
--- /dev/null
+++ b/modules/chibi/cp_player_data_notes.cpp
@@ -0,0 +1,345 @@
+/*************************************************************************/
+/* cp_player_data_notes.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+#include "cp_sample_manager.h"
+
+#define RANDOM_MAX 2147483647
+
+static inline int32_t cp_random_generate(int32_t *seed) {
+ int32_t k;
+ int32_t s = (int32_t)(*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = (int32_t)s;
+ return (int32_t)(s & RANDOM_MAX);
+}
+
+
+void CPPlayer::process_new_note(int p_track,uint8_t p_note) { // if there's really a new note....
+
+ if (control.channel[p_track].real_note!=255) {
+ control.channel[p_track].old_note=control.channel[p_track].real_note;
+
+ }
+
+ control.channel[p_track].real_note=p_note;
+
+ control.channel[p_track].kick=KICK_NOTE;
+
+ control.channel[p_track].sample_start_index=-1;
+ control.channel[p_track].sliding=0;
+ control.channel[p_track].row_has_note=true;
+ control.channel[p_track].last_event_usecs=song_usecs;
+
+ if (control.channel[p_track].panbrello_type) control.channel[p_track].panbrello_position=0;
+}
+
+bool CPPlayer::process_new_instrument(int p_track,uint8_t p_instrument) {
+
+// bool different_instrument=false;
+ ERR_FAIL_INDEX_V(p_instrument,CPSong::MAX_INSTRUMENTS,false);
+
+ if ( song->has_instruments() ) {
+
+
+ control.channel[p_track].instrument_ptr=song->get_instrument(p_instrument);
+ } else {
+
+ control.channel[p_track].instrument_ptr=NULL;
+ }
+
+ control.channel[p_track].retrig_counter=0;
+ control.channel[p_track].tremor_position=0;
+ control.channel[p_track].sample_offset_fine=0;
+ int old_instr_index=control.channel[p_track].instrument_index;
+ control.channel[p_track].instrument_index=p_instrument;
+
+ return (old_instr_index!=p_instrument);
+
+
+}
+
+
+ // returns if it was able to process
+bool CPPlayer::process_note_and_instrument(int p_track,int p_note,int p_instrument) {
+
+ bool aux_result;
+ aux_result=false;
+ CPSample *aux_sample=0; // current sample
+ int dest_sample_index;
+ bool new_instrument=false;
+
+ control.channel[p_track].row_has_note=false; // wise man says.. "we dont have a note... until we really know we have a note".
+ control.channel[p_track].new_instrument=false;
+
+ if ( (p_note<0) && (p_instrument<0) ) return aux_result; // nothing to do here
+ if ( (p_note==255) && (p_instrument==255) ) return aux_result;
+
+ if ( (p_note>=0) && (p_note<120) ) {
+
+ process_new_note(p_track,p_note);
+
+ } else if (p_note==CPNote::CUT) {
+
+ control.channel[p_track].aux_volume=0;
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+ control.channel[p_track].note_end_flags|=END_NOTE_KILL;
+ return aux_result;
+
+ } else if ((p_note==CPNote::OFF) && (song->has_instruments())) {
+
+ if (control.channel[p_track].instrument_ptr!=NULL) {
+
+ control.channel[p_track].note_end_flags|=END_NOTE_OFF;
+
+ if (!control.channel[p_track].instrument_ptr->get_volume_envelope()->is_enabled() || control.channel[p_track].instrument_ptr->get_volume_envelope()->is_loop_enabled()) {
+
+ control.channel[p_track].note_end_flags|=END_NOTE_FADE;
+ }
+ }
+
+ return aux_result;
+ } else return aux_result; // invalid note!
+
+
+ if ( (p_instrument>=0) && (p_instrument<CPSong::MAX_INSTRUMENTS)) {
+ new_instrument=process_new_instrument(p_track,p_instrument);
+
+ if ( song->has_instruments() ) {
+ // If we're in instrument mode...
+ if ( control.channel[p_track].instrument_ptr->get_sample_number(control.channel[p_track].real_note) >= CPSong::MAX_SAMPLES) {
+
+ control.channel[p_track].kick=KICK_NOTHING;
+ return aux_result;
+
+ } else {
+ dest_sample_index=control.channel[p_track].instrument_ptr->get_sample_number(control.channel[p_track].real_note);
+ control.channel[p_track].note=control.channel[p_track].instrument_ptr->get_note_number(control.channel[p_track].real_note);
+ }
+
+ } else {
+ // If we're in sample mode...
+ dest_sample_index=control.channel[p_track].instrument_index;
+ control.channel[p_track].note=control.channel[p_track].real_note;
+ }
+
+ control.channel[p_track].sample_index=dest_sample_index;
+ aux_sample=song->get_sample(dest_sample_index);
+
+ if (!CPSampleManager::get_singleton()->check( aux_sample->get_sample_data() )) {
+ /* INVALID SAMPLE */
+ control.channel[p_track].kick=KICK_NOTHING;
+ return aux_result;
+
+ }
+
+ aux_sample=song->get_sample(dest_sample_index);
+ } else {
+
+
+ if (!control.channel[p_track].sample_ptr)
+ return aux_result;
+
+ if (song->has_instruments()) {
+
+ if (!control.channel[p_track].instrument_ptr)
+ return aux_result;
+
+ control.channel[p_track].note=control.channel[p_track].instrument_ptr->get_note_number(control.channel[p_track].real_note);
+
+ } else {
+
+ control.channel[p_track].note=control.channel[p_track].real_note;
+
+ }
+
+ aux_sample=control.channel[p_track].sample_ptr;
+
+ }
+
+
+
+ if (p_instrument>=CPSong::MAX_INSTRUMENTS && control.channel[p_track].sample_ptr!=aux_sample) {
+
+ control.channel[p_track].new_instrument=(control.channel[p_track].period>0);
+ }
+
+ control.channel[p_track].sample_ptr=aux_sample;
+
+ /* channel or instrument determined panning ? */
+
+ control.channel[p_track].panning=control.channel[p_track].channel_panning;
+
+ /* set filter,if any ? */
+
+
+ if (aux_sample->is_pan_enabled()) {
+
+ control.channel[p_track].panning=(int)aux_sample->get_pan()*255/64;
+
+ } else if ( song->has_instruments() && (control.channel[p_track].instrument_ptr->is_pan_default_enabled()) ) {
+
+ control.channel[p_track].panning=(int)control.channel[p_track].instrument_ptr->get_pan_default_amount()*255/64;
+ }
+
+
+ if (song->has_instruments()) {
+
+
+ /* Pitch-Pan Separation */
+ if ((control.channel[p_track].instrument_ptr->get_pan_pitch_separation()!=0) && (control.channel[p_track].channel_panning!=PAN_SURROUND)){
+
+ control.channel[p_track].panning+=((control.channel[p_track].real_note-control.channel[p_track].instrument_ptr->get_pan_pitch_center())*control.channel[p_track].instrument_ptr->get_pan_pitch_separation())/8;
+
+ if (control.channel[p_track].panning<PAN_LEFT) control.channel[p_track].panning=PAN_LEFT;
+ if (control.channel[p_track].panning>PAN_RIGHT) control.channel[p_track].panning=PAN_RIGHT;
+ }
+
+ /* Random Volume Variation */
+ if (control.channel[p_track].instrument_ptr->get_volume_random_variation()>0) {
+
+ control.channel[p_track].random_volume_variation=100-(cp_random_generate(&control.random_seed) % control.channel[p_track].instrument_ptr->get_volume_random_variation());
+
+ } else {
+
+ control.channel[p_track].random_volume_variation=100;
+ }
+
+
+ /* Random Pan Variation */
+ if ((control.channel[p_track].instrument_ptr->get_pan_random_variation()>0) && (control.channel[p_track].panning!=PAN_SURROUND)){
+
+ int aux_pan_modifier;
+
+ aux_pan_modifier=(cp_random_generate(&control.random_seed) % (control.channel[p_track].instrument_ptr->get_pan_random_variation() << 2));
+ if ((cp_random_generate(&control.random_seed) % 2)==1) aux_pan_modifier=0-aux_pan_modifier; /* it's 5am, let me sleep :) */
+
+ control.channel[p_track].panning+=aux_pan_modifier;
+
+ if (control.channel[p_track].panning<PAN_LEFT) control.channel[p_track].panning=PAN_LEFT;
+ if (control.channel[p_track].panning>PAN_RIGHT) control.channel[p_track].panning=PAN_RIGHT;
+
+
+ }
+
+ /*filter*/
+
+ if (control.channel[p_track].instrument_ptr->filter_use_default_cutoff()) {
+
+ control.channel[p_track].filter.it_cutoff=control.channel[p_track].instrument_ptr->get_filter_default_cutoff()*2;
+
+ }
+
+ if (control.channel[p_track].instrument_ptr->filter_use_default_resonance()) {
+
+ control.channel[p_track].filter.it_reso=control.channel[p_track].instrument_ptr->get_filter_default_resonance()*2;
+
+ }
+
+ /*envelopes*/
+
+
+ control.channel[p_track].volume_envelope_on=control.channel[p_track].instrument_ptr->get_volume_envelope()->is_enabled();
+ control.channel[p_track].panning_envelope_on=control.channel[p_track].instrument_ptr->get_pan_envelope()->is_enabled();
+ control.channel[p_track].pitch_envelope_on=control.channel[p_track].instrument_ptr->get_pitch_filter_envelope()->is_enabled();
+ control.channel[p_track].NNA_type=control.channel[p_track].instrument_ptr->get_NNA_type();
+ control.channel[p_track].duplicate_check_type=control.channel[p_track].instrument_ptr->get_DC_type();
+ control.channel[p_track].duplicate_check_action=control.channel[p_track].instrument_ptr->get_DC_action();
+
+
+ } else {
+
+ control.channel[p_track].NNA_type=CPInstrument::NNA_NOTE_CUT;
+ control.channel[p_track].duplicate_check_type=CPInstrument::DCT_DISABLED;
+ control.channel[p_track].duplicate_check_action=CPInstrument::DCA_NOTE_CUT;
+ }
+
+
+ if (p_instrument<CPSong::MAX_INSTRUMENTS) { // instrument change
+
+ control.channel[p_track].volume=control.channel[p_track].aux_volume=aux_sample->get_default_volume();
+
+ }
+
+
+ control.channel[p_track].slide_to_period=control.channel[p_track].aux_period=get_period((uint16_t)(control.channel[p_track].note)<<1,CPSampleManager::get_singleton()->get_c5_freq( (aux_sample->get_sample_data())));
+
+ control.channel[p_track].note_end_flags=END_NOTE_NOTHING; /* clears flags */
+
+ return true;
+}
+
+void CPPlayer::process_volume_column(int p_track,uint8_t p_volume) {
+
+ control.channel[p_track].current_volume_command=CPNote::EMPTY;
+ control.channel[p_track].current_volume_parameter=CPNote::EMPTY;
+
+ if (p_volume<65) { // VOLUME
+
+ control.channel[p_track].aux_volume=p_volume;
+ } else if (p_volume<125) { // Volume Command
+
+
+ control.channel[p_track].current_volume_command=(p_volume-65) / 10;
+ control.channel[p_track].current_volume_parameter=(p_volume-65) % 10;
+ } else if (p_volume<193) { // PAN
+
+ control.channel[p_track].channel_panning=(p_volume-128)*PAN_RIGHT/64;
+ control.channel[p_track].panning=control.channel[p_track].channel_panning;
+
+ } else if (p_volume<213) { //More volume Commands
+
+ control.channel[p_track].current_volume_command=((p_volume-193) / 10)+6;
+ control.channel[p_track].current_volume_parameter=(p_volume-193) % 10;
+ }
+}
+
+
+void CPPlayer::process_note(int p_track,CPNote p_note) {
+
+ if ( p_note.note!=CPNote::SCRIPT ) {
+
+ process_note_and_instrument(p_track,p_note.note,p_note.instrument);
+ process_volume_column(p_track,p_note.volume);
+ control.channel[p_track].current_command=p_note.command;
+ control.channel[p_track].current_parameter=p_note.parameter;
+
+ } else {
+
+ CPNote n = song->get_pattern( control.position.current_pattern )->get_transformed_script_note( p_track, control.position.current_row );
+ process_note( p_track, n );
+
+ song->get_pattern( control.position.current_pattern )->scripted_clone( p_track, control.position.current_row );
+ }
+}
diff --git a/modules/chibi/cp_player_data_utils.cpp b/modules/chibi/cp_player_data_utils.cpp
new file mode 100644
index 0000000000..170a849863
--- /dev/null
+++ b/modules/chibi/cp_player_data_utils.cpp
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* cp_player_data_utils.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_player_data.h"
+uint8_t CPPlayer::vibrato_table[32]={
+ 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
+ 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
+};
+
+uint8_t CPPlayer::auto_vibrato_table[128]={
+ 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
+ 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
+ 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
+ 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
+ 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
+ 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
+ 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
+ 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
+};
+
+
+int8_t CPPlayer::panbrello_table[256]={
+ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+ 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+ 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+ 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+ 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
+ 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+ -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+ -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+ -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+ -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+ -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+ -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
+};
+
+
+
+
+
+
+int32_t CPPlayer::get_period(uint16_t p_note,int32_t p_c5speed) {
+
+ if (song->has_linear_slides()) {
+
+ return CPTables::get_linear_period(p_note,0);
+ } else {
+
+
+ return CPTables::get_log_period(p_note>>1,p_c5speed >>1);
+ }
+}
+
+
+int32_t CPPlayer::get_frequency(int32_t period) {
+
+ if (song->has_linear_slides()) {
+
+ return CPTables::get_linear_frequency(period);
+ } else {
+
+ return CPTables::get_old_frequency(period);
+ }
+}
+
+int CPPlayer::find_empty_voice() {
+
+ int i;
+ int min_priority,min_priority_chan=0,priority;
+
+ for (i=0;i<control.max_voices;i++) {
+
+ if ( ((voice[i].kick==KICK_NOTHING)||(voice[i].kick==KICK_ENVELOPE))&&!mixer->is_voice_active(i) ) {
+
+ return i;
+
+ }
+ }
+
+ // todo more
+
+ for (i=0;i<control.max_voices;i++) {
+ /* allow us to take over a nonexisting sample */
+// if ((voice[i].s==NULL)
+// return k;
+
+ if ((voice[i].kick==KICK_NOTHING)||(voice[i].kick==KICK_ENVELOPE)) {
+
+ priority=voice[i].total_volume<<((CPSampleManager::get_singleton()->get_loop_type( voice[i].sample_ptr->get_sample_data())!=CP_LOOP_NONE)?1:0);
+
+ if ((voice[i].has_master_channel)&&(&voice[i]==voice[i].master_channel->slave_voice)) {
+
+ priority<<=2;
+
+ }
+
+ if ((i==0) || (priority<min_priority)) {
+ min_priority=priority;
+ min_priority_chan=i;
+ }
+ }
+ }
+
+ if (min_priority>8000*7) return -1; /* what the fuck is this? */
+
+ return min_priority_chan;
+}
+
diff --git a/modules/chibi/cp_sample.cpp b/modules/chibi/cp_sample.cpp
new file mode 100644
index 0000000000..55c2c910a5
--- /dev/null
+++ b/modules/chibi/cp_sample.cpp
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* cp_sample.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_sample.h"
+
+const char * CPSample::get_name() const {
+
+ return name;
+}
+void CPSample::set_name(const char *p_name) {
+
+ if (p_name==NULL) {
+ name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<NAME_MAX_LEN;i++) {
+
+
+ name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ name[NAME_MAX_LEN-1]=0; /* just in case */
+
+}
+
+void CPSample::set_default_volume(uint8_t p_vol) {
+
+ default_volume=p_vol;
+}
+uint8_t CPSample::get_default_volume() const{
+
+ return default_volume;
+}
+
+void CPSample::set_global_volume(uint8_t p_vol) {
+
+ global_volume=p_vol;
+}
+uint8_t CPSample::get_global_volume() const{
+
+ return global_volume;
+}
+
+void CPSample::set_pan_enabled(bool p_vol) {
+
+ pan_enabled=p_vol;
+}
+bool CPSample::is_pan_enabled() const{
+
+ return pan_enabled;
+}
+
+void CPSample::set_pan(uint8_t p_pan) {
+
+ pan=p_pan;
+
+}
+uint8_t CPSample::get_pan() const{
+
+ return pan;
+}
+
+
+void CPSample::set_vibrato_type(VibratoType p_vibrato_type) {
+
+ vibrato_type=p_vibrato_type;
+}
+CPSample::VibratoType CPSample::get_vibrato_type() const{
+
+ return vibrato_type;
+}
+
+void CPSample::set_vibrato_speed(uint8_t p_vibrato_speed) {
+
+ vibrato_speed=p_vibrato_speed;
+}
+uint8_t CPSample::get_vibrato_speed() const {
+
+ return vibrato_speed;
+}
+
+void CPSample::set_vibrato_depth(uint8_t p_vibrato_depth) {
+
+ vibrato_depth=p_vibrato_depth;
+}
+uint8_t CPSample::get_vibrato_depth() const{
+
+ return vibrato_depth;
+}
+
+void CPSample::set_vibrato_rate(uint8_t p_vibrato_rate) {
+
+ vibrato_rate=p_vibrato_rate;
+}
+uint8_t CPSample::get_vibrato_rate() const{
+
+ return vibrato_rate;
+}
+
+void CPSample::set_sample_data(CPSample_ID p_ID) {
+
+ id=p_ID;
+}
+CPSample_ID CPSample::get_sample_data() const{
+
+ return id;
+}
+
+void CPSample::operator=(const CPSample &p_sample) {
+
+ copy_from(p_sample);
+}
+void CPSample::copy_from(const CPSample &p_sample) {
+
+ reset();
+ set_name(p_sample.get_name());
+
+ default_volume=p_sample.default_volume;
+ global_volume=p_sample.global_volume;
+
+ pan_enabled=p_sample.pan_enabled;
+ pan=p_sample.pan;
+
+ vibrato_type=p_sample.vibrato_type;
+ vibrato_speed=p_sample.vibrato_speed;
+ vibrato_depth=p_sample.vibrato_depth;
+ vibrato_rate=p_sample.vibrato_rate;
+
+ if (CPSampleManager::get_singleton() && !p_sample.id.is_null())
+ CPSampleManager::get_singleton()->copy_to( p_sample.id, id );
+}
+
+
+
+
+
+void CPSample::reset() {
+
+
+ name[0]=0;
+
+ default_volume=64;
+ global_volume=64;
+
+ pan_enabled=false;
+ pan=32;
+
+ vibrato_type=VIBRATO_SINE;
+ vibrato_speed=0;
+ vibrato_depth=0;
+ vibrato_rate=0;
+
+ if (!id.is_null() && CPSampleManager::get_singleton())
+ CPSampleManager::get_singleton()->destroy( id );
+
+ id=CPSample_ID();
+
+}
+
+CPSample::CPSample(const CPSample&p_from) {
+
+ reset();
+ copy_from(p_from);
+}
+CPSample::CPSample() {
+
+ reset();
+}
+
+CPSample::~CPSample() {
+
+ reset();
+}
diff --git a/modules/chibi/cp_sample.h b/modules/chibi/cp_sample.h
new file mode 100644
index 0000000000..4b3d218106
--- /dev/null
+++ b/modules/chibi/cp_sample.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* cp_sample.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CPSAMPLE_H
+#define CPSAMPLE_H
+
+
+#include "cp_config.h"
+#include "cp_sample_manager.h"
+class CPSample {
+
+public:
+ enum VibratoType {
+ VIBRATO_SINE,
+ VIBRATO_SAW,
+ VIBRATO_SQUARE,
+ VIBRATO_RANDOM
+
+ };
+
+private:
+
+ enum { NAME_MAX_LEN=26 };
+
+ char name[NAME_MAX_LEN];
+
+ uint8_t default_volume; /* 0.. 64 */
+ uint8_t global_volume; /* 0.. 64 */
+
+ bool pan_enabled;
+ uint8_t pan; /* 0.. 64 */
+
+ VibratoType vibrato_type;
+ uint8_t vibrato_speed; /* 0.. 64 */
+ uint8_t vibrato_depth; /* 0.. 64 */
+ uint8_t vibrato_rate; /* 0.. 64 */
+
+ CPSample_ID id;
+
+ void copy_from(const CPSample &p_sample);
+public:
+
+
+ void operator=(const CPSample &p_sample);
+
+ const char * get_name() const;
+ void set_name(const char *p_name);
+
+ void set_default_volume(uint8_t p_vol);
+ uint8_t get_default_volume() const;
+
+ void set_global_volume(uint8_t p_vol);
+ uint8_t get_global_volume() const;
+
+ void set_pan_enabled(bool p_vol);
+ bool is_pan_enabled() const;
+
+ void set_pan(uint8_t p_pan);
+ uint8_t get_pan() const;
+
+ void set_vibrato_type(VibratoType p_vibrato_type);
+ VibratoType get_vibrato_type() const;
+
+ void set_vibrato_speed(uint8_t p_vibrato_speed) ;
+ uint8_t get_vibrato_speed() const;
+
+ void set_vibrato_depth(uint8_t p_vibrato_depth);
+ uint8_t get_vibrato_depth() const;
+
+ void set_vibrato_rate(uint8_t p_vibrato_rate);
+ uint8_t get_vibrato_rate() const;
+
+ void set_sample_data(CPSample_ID);
+ CPSample_ID get_sample_data() const;
+
+ void reset();
+
+ CPSample(const CPSample&p_from);
+ CPSample();
+ ~CPSample();
+
+};
+
+
+
+
+#endif
diff --git a/modules/chibi/cp_sample_defs.h b/modules/chibi/cp_sample_defs.h
new file mode 100644
index 0000000000..169963c98e
--- /dev/null
+++ b/modules/chibi/cp_sample_defs.h
@@ -0,0 +1,97 @@
+/*************************************************************************/
+/* cp_sample_defs.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_SAMPLE_DEFS_H
+#define CP_SAMPLE_DEFS_H
+
+#include "cp_config.h"
+
+enum CPSample_Loop_Type {
+
+ CP_LOOP_NONE,
+ CP_LOOP_FORWARD,
+ CP_LOOP_BIDI
+};
+
+//#define INVALID_SAMPLE_ID -1
+
+#define CP_MIXING_FRAC_BITS_MACRO 13
+#define CP_MIXING_FRAC_BITS_TEXT "13"
+// 1<<9 - 1
+#define CP_MIXING_FRAC_BITS_MASK_TEXT "8191"
+
+enum CPMixConstants {
+ CP_MIXING_FRAC_BITS=CP_MIXING_FRAC_BITS_MACRO,
+ CP_MIXING_FRAC_LENGTH=(1<<CP_MIXING_FRAC_BITS),
+ CP_MIXING_FRAC_MASK=CP_MIXING_FRAC_LENGTH-1,
+ CP_MIXING_VOL_FRAC_BITS=8,
+ CP_MIXING_FREQ_FRAC_BITS=8
+};
+
+enum CPFilterConstants {
+ CP_FILTER_SHIFT=16,
+ CP_FILTER_LENGTH=(1<<CP_FILTER_SHIFT)
+};
+
+
+enum CPInterpolationType {
+ CP_INTERPOLATION_RAW,
+ CP_INTERPOLATION_LINEAR,
+ CP_INTERPOLATION_CUBIC
+};
+
+enum CPPanConstants {
+
+ CP_PAN_BITS=8, // 0 .. 256
+ CP_PAN_LEFT=0,
+ CP_PAN_RIGHT=((1<<CP_PAN_BITS)-1), // 255
+ CP_PAN_CENTER=CP_PAN_RIGHT/2, // 128
+ CP_PAN_SURROUND=512
+};
+
+enum CPMixerVolConstants {
+ CP_VOL_MAX=512,
+ CP_VOL_RAMP_BITS=9,
+ CP_VOL_SHIFT=2
+
+
+};
+
+enum CPStereoCannels {
+ CP_CHAN_LEFT,
+ CP_CHAN_RIGHT
+};
+
+#define CP_FIRST_SAMPLE_DECLICK_THRESHOLD 1000
+#define CP_FIRST_SAMPLE_RAMP_LEN 32
+
+typedef signed char CPFrame8;
+typedef signed short CPFrame16;
+
+
+#endif
diff --git a/modules/chibi/cp_sample_manager.cpp b/modules/chibi/cp_sample_manager.cpp
new file mode 100644
index 0000000000..5c2988e3f9
--- /dev/null
+++ b/modules/chibi/cp_sample_manager.cpp
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* cp_sample_manager.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_sample_manager.h"
+
+
+CPSampleManager * CPSampleManager::singleton=NULL;
+
+
+void CPSampleManager::copy_to(CPSample_ID p_from,CPSample_ID &p_to) {
+
+ ERR_FAIL_COND(!check( p_from ));
+
+
+ if (p_to.is_null()) {
+
+ p_to=create( is_16bits( p_from), is_stereo( p_from), get_size(p_from));
+ } else {
+
+ recreate( p_to, is_16bits( p_from), is_stereo( p_from), get_size(p_from));
+
+ }
+
+ int len=get_size( p_from );
+ int ch=is_stereo( p_from ) ? 2 : 1;
+
+ for (int c=0;c<ch;c++) {
+
+ for (int i=0;i<len;i++) {
+
+ int16_t s=get_data( p_from, i, c );
+ set_data( p_to, i, s, c );
+ }
+ }
+
+ set_loop_type( p_to, get_loop_type( p_from ) );
+ set_loop_begin( p_to, get_loop_begin( p_from ) );
+ set_loop_end( p_to, get_loop_end( p_from ) );
+ set_c5_freq( p_to, get_c5_freq( p_from ) );
+
+
+
+}
+
+CPSampleManager::CPSampleManager() {
+
+ singleton=this;
+}
+
+CPSampleManager *CPSampleManager::get_singleton() {
+
+ return singleton;
+}
diff --git a/modules/chibi/cp_sample_manager.h b/modules/chibi/cp_sample_manager.h
new file mode 100644
index 0000000000..74bcafc0cf
--- /dev/null
+++ b/modules/chibi/cp_sample_manager.h
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* cp_sample_manager.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CP_SAMPLE_MANAGER_H
+#define CP_SAMPLE_MANAGER_H
+
+#include "cp_config.h"
+#include "cp_sample_defs.h"
+
+
+
+/**
+@author Juan Linietsky
+*/
+
+
+/* abstract base CPSample_ID class */
+
+struct CPSample_ID {
+
+ void *_private;
+
+ bool operator==(const CPSample_ID&p_other) const { return _private==p_other._private; }
+ bool operator!=(const CPSample_ID&p_other) const { return _private!=p_other._private; }
+ bool is_null() const { return _private==0; }
+ CPSample_ID(void *p_private=0) { _private=p_private; };
+};
+
+
+class CPSampleManager {
+
+ static CPSampleManager * singleton;
+
+public:
+
+ /* get the singleton instance */
+ static CPSampleManager *get_singleton();
+
+ virtual void copy_to(CPSample_ID p_from,CPSample_ID &p_to); ///< if p_to is null, it gets created
+
+ virtual CPSample_ID create(bool p_16bits,bool p_stereo,int32_t p_len)=0;
+ virtual void recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len)=0;
+ virtual void destroy(CPSample_ID p_id)=0;
+ virtual bool check(CPSample_ID p_id)=0; // return false if invalid
+
+ virtual void set_c5_freq(CPSample_ID p_id,int32_t p_freq)=0;
+ virtual void set_loop_begin(CPSample_ID p_id,int32_t p_begin)=0;
+ virtual void set_loop_end(CPSample_ID p_id,int32_t p_end)=0;
+ virtual void set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type)=0;
+ virtual void set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len)=0;
+
+
+ virtual int32_t get_loop_begin(CPSample_ID p_id)=0;
+ virtual int32_t get_loop_end(CPSample_ID p_id)=0;
+ virtual CPSample_Loop_Type get_loop_type(CPSample_ID p_id)=0;
+ virtual int32_t get_c5_freq(CPSample_ID p_id)=0;
+ virtual int32_t get_size(CPSample_ID p_id)=0;
+ virtual bool is_16bits(CPSample_ID p_id)=0;
+ virtual bool is_stereo(CPSample_ID p_id)=0;
+ virtual bool lock_data(CPSample_ID p_id)=0;
+ virtual void *get_data(CPSample_ID p_id)=0; /* WARNING: Not all sample managers
+may be able to implement this, it depends on the mixer in use! */
+ virtual int16_t get_data(CPSample_ID p_id, int p_sample, int p_channel=0)=0; /// Does not need locking
+ virtual void set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel=0)=0; /// Does not need locking
+ virtual void unlock_data(CPSample_ID p_id)=0;
+
+ virtual void get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len)=0;
+
+ CPSampleManager();
+ virtual ~CPSampleManager(){}
+
+};
+
+#endif
diff --git a/modules/chibi/cp_song.cpp b/modules/chibi/cp_song.cpp
new file mode 100644
index 0000000000..4aa1a4228d
--- /dev/null
+++ b/modules/chibi/cp_song.cpp
@@ -0,0 +1,957 @@
+/*************************************************************************/
+/* cp_song.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "cp_song.h"
+
+void CPSong::set_name(const char *p_name) {
+
+ if (p_name==NULL) {
+ variables.name[0]=0;
+ return;
+ }
+
+
+ bool done=false;
+ for (int i=0;i<MAX_SONG_NAME;i++) {
+
+
+ variables.name[i]=done?0:p_name[i];
+ if (!done && p_name[i]==0)
+ done=true;
+ }
+
+ variables.name[MAX_SONG_NAME-1]=0; /* just in case */
+}
+
+const char * CPSong::get_name() {
+
+ return variables.name;
+
+}
+
+void CPSong::set_message(const char *p_message) {
+
+ if (p_message==NULL) {
+ variables.message[0]=0;
+ return;
+ }
+
+ bool done=false;
+ for (int i=0;i<MAX_MESSAGE_LEN;i++) {
+
+
+ variables.message[i]=done?0:p_message[i];
+ if (!done && p_message[i]==0)
+ done=true;
+ }
+
+ variables.message[MAX_MESSAGE_LEN-1]=0; /* just in case */
+}
+
+const char * CPSong::get_message() {
+
+ return variables.message;
+
+}
+
+void CPSong::set_row_highlight_minor(int p_hl_minor) {
+
+ variables.row_highlight_minor=p_hl_minor;
+}
+int CPSong::get_row_highlight_minor() {
+
+ return variables.row_highlight_minor;
+}
+
+void CPSong::set_row_highlight_major(int p_hl_major) {
+
+ variables.row_highlight_major=p_hl_major;
+
+
+} /* 0 .. 256 */
+int CPSong::get_row_highlight_major() {
+
+ return variables.row_highlight_major;
+
+
+} /* 0 .. 256 */
+
+void CPSong::set_mixing_volume(int p_mix_volume) {
+
+
+ variables.mixing_volume=p_mix_volume;
+} /* 0 .. 128 */
+int CPSong::get_mixing_volume() {
+
+ return variables.mixing_volume;
+
+} /* 0 .. 128 */
+
+void CPSong::set_global_volume(int p_global_volume) {
+
+
+ initial_variables.global_volume=p_global_volume;
+
+} /* 0 .. 128 */
+int CPSong::get_global_volume() {
+
+ return initial_variables.global_volume;
+
+} /* 0 .. 128 */
+
+void CPSong::set_stereo_separation(int p_separation) {
+
+ variables.stereo_separation=p_separation;
+
+} /* 0 .. 128 */
+int CPSong::get_stereo_separation() {
+
+ return variables.stereo_separation;
+} /* 0 .. 128 */
+
+void CPSong::set_stereo(bool p_stereo) {
+
+ variables.use_stereo=p_stereo;
+
+}
+bool CPSong::is_stereo() {
+
+ return variables.use_stereo;
+}
+
+void CPSong::set_instruments(bool p_instruments) {
+
+ variables.use_instruments=p_instruments;
+
+
+}
+bool CPSong::has_instruments() {
+
+
+ return variables.use_instruments;
+
+}
+
+void CPSong::set_linear_slides(bool p_linear_slides) {
+
+ variables.use_linear_slides=p_linear_slides;
+
+
+}
+bool CPSong::has_linear_slides() {
+
+ return variables.use_linear_slides;
+
+
+}
+
+void CPSong::set_old_effects(bool p_old_effects) {
+
+ variables.old_effects=p_old_effects;
+
+
+}
+bool CPSong::has_old_effects() {
+
+ return variables.old_effects;
+}
+
+void CPSong::set_compatible_gxx(bool p_compatible_gxx) {
+
+
+ variables.compatible_gxx=p_compatible_gxx;
+}
+bool CPSong::has_compatible_gxx() {
+
+ return variables.compatible_gxx;
+
+}
+
+void CPSong::set_speed(int p_speed) {
+
+ CP_ERR_COND(p_speed<MIN_SPEED);
+ CP_ERR_COND(p_speed>MAX_SPEED);
+
+ initial_variables.speed=p_speed;
+
+} /* 1 .. 255 */
+int CPSong::get_speed() {
+
+ return initial_variables.speed;
+
+} /* 1 .. 255 */
+
+void CPSong::set_tempo(int p_tempo) {
+
+ CP_ERR_COND( p_tempo<MIN_TEMPO );
+ CP_ERR_COND( p_tempo>MAX_TEMPO );
+
+ initial_variables.tempo=p_tempo;
+
+} /* MIN_TEMPO .. MAX_TEMPO */
+int CPSong::get_tempo() {
+
+ return initial_variables.tempo;
+
+
+} /* MIN_TEMPO .. MAX_TEMPO */
+
+void CPSong::set_channel_pan(int p_channel,int p_pan) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_pan,CHANNEL_MAX_PAN+1);
+
+ initial_variables.channel[p_channel].pan=p_pan;
+
+} /* 0 .. CHANNEL_MAX_PAN */
+int CPSong::get_channel_pan(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].pan;
+}
+
+void CPSong::set_channel_volume(int p_channel,int p_volume) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_volume,CHANNEL_MAX_VOLUME+1);
+
+
+ initial_variables.channel[p_channel].volume=p_volume;
+
+
+} /* 0 .. CHANNEL_MAX_VOLUME */
+
+
+int CPSong::get_channel_volume(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].volume;
+
+}
+
+void CPSong::set_channel_chorus(int p_channel,int p_chorus) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_chorus,CHANNEL_MAX_CHORUS+1);
+
+
+ initial_variables.channel[p_channel].chorus=p_chorus;
+
+
+} /* 0 .. CHANNEL_MAX_CHORUS */
+
+
+int CPSong::get_channel_chorus(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].chorus;
+
+}
+
+void CPSong::set_channel_reverb(int p_channel,int p_reverb) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ CP_FAIL_INDEX(p_reverb,CHANNEL_MAX_REVERB+1);
+
+
+ initial_variables.channel[p_channel].reverb=p_reverb;
+
+
+} /* 0 .. CHANNEL_MAX_CHORUS */
+
+
+int CPSong::get_channel_reverb(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,-1);
+
+ return initial_variables.channel[p_channel].reverb;
+
+}
+
+void CPSong::set_channel_surround(int p_channel,bool p_surround) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+ initial_variables.channel[p_channel].surround=p_surround;
+
+}
+bool CPSong::is_channel_surround(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
+
+ return initial_variables.channel[p_channel].surround;
+
+
+}
+
+void CPSong::set_channel_mute(int p_channel,bool p_mute) {
+
+ CP_FAIL_INDEX(p_channel,CPPattern::WIDTH);
+
+ initial_variables.channel[p_channel].mute=p_mute;
+
+}
+bool CPSong::is_channel_mute(int p_channel) {
+
+ CP_FAIL_INDEX_V(p_channel,CPPattern::WIDTH,false);
+
+ return initial_variables.channel[p_channel].mute;
+
+}
+
+/* arrays of stuff */
+
+CPPattern* CPSong::get_pattern(int p_pattern) {
+
+ CP_FAIL_INDEX_V(p_pattern,MAX_PATTERNS, NULL);
+
+ return &pattern[p_pattern];
+
+}
+CPSample* CPSong::get_sample(int p_sample) {
+
+ CP_FAIL_INDEX_V(p_sample,MAX_SAMPLES,NULL);
+
+ return &sample[p_sample];
+
+
+}
+CPInstrument* CPSong::get_instrument(int p_instrument) {
+
+
+ CP_FAIL_INDEX_V(p_instrument,MAX_INSTRUMENTS,NULL);
+
+ return &instrument[p_instrument];
+
+}
+
+int CPSong::get_order(int p_order) {
+
+ CP_FAIL_INDEX_V(p_order,MAX_ORDERS,CP_ORDER_NONE);
+
+
+ return order[p_order];
+
+}
+void CPSong::set_order(int p_order,int p_pattern) {
+
+ CP_FAIL_INDEX(p_order,MAX_ORDERS);
+
+ order[p_order]=p_pattern;
+
+}
+
+
+void CPSong::clear_instrument_with_samples(int p_instrument) {
+
+ CPInstrument *ins = get_instrument( p_instrument );
+ if (!ins)
+ return;
+
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ CPSample *s=get_sample( ins->get_sample_number( i ) );
+
+ if (!s)
+ continue;
+
+ if (s->get_sample_data().is_null())
+ continue;
+
+ s->reset();
+ }
+ ins->reset();
+}
+
+void CPSong::make_instrument_from_sample(int p_sample) {
+
+ if (!has_instruments())
+ return;
+ CP_ERR_COND(!get_sample( p_sample ));
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++) {
+
+
+ CPInstrument *ins=get_instrument(i);
+
+ bool empty_slot=true;
+ for (int n=0;n<CPNote::NOTES;n++) {
+
+ if (ins->get_sample_number(n)<MAX_SAMPLES) {
+
+ empty_slot=false;
+ break;
+ }
+ }
+
+ if (!empty_slot)
+ continue;
+
+ for (int n=0;n<CPNote::NOTES;n++) {
+
+ ins->set_sample_number(n,p_sample);
+ ins->set_note_number(n,n);
+ }
+
+ ins->set_name( get_sample( p_sample )->get_name() );
+ break;
+ }
+
+}
+
+void CPSong::make_instruments_from_samples() {
+
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ CPInstrument *ins=get_instrument( i );
+
+ if (!ins)
+ continue;
+
+ ins->reset();
+
+ CPSample *s=get_sample( i );
+
+ if (!s)
+ continue;
+
+ ins->set_name( s->get_name() );
+
+ if (s->get_sample_data().is_null())
+ continue;
+
+
+
+
+ for(int j=0;j<CPNote::NOTES;j++)
+ ins->set_sample_number( j, i );
+
+
+
+ }
+}
+
+void CPSong::reset(bool p_clear_patterns,bool p_clear_samples,bool p_clear_instruments,bool p_clear_variables) {
+
+ if (p_clear_variables) {
+ variables.name[0]=0;
+ variables.message[0]=0;
+ variables.row_highlight_major=16;
+ variables.row_highlight_minor=4;
+ variables.mixing_volume=48;
+ variables.old_effects=false;
+ if (p_clear_instruments) //should not be cleared, if not clearing instruments!!
+ variables.use_instruments=false;
+ variables.stereo_separation=128;
+ variables.use_linear_slides=true;
+ variables.use_stereo=true;
+
+ initial_variables.global_volume=128;
+ initial_variables.speed=6;
+ initial_variables.tempo=125;
+
+ for (int i=0;i<CPPattern::WIDTH;i++) {
+
+ initial_variables.channel[i].pan=32;
+ initial_variables.channel[i].volume=CHANNEL_MAX_VOLUME;
+ initial_variables.channel[i].mute=false;
+ initial_variables.channel[i].surround=false;
+ initial_variables.channel[i].chorus=0;
+ initial_variables.channel[i].reverb=0;
+
+ }
+
+ effects.chorus.delay_ms=6;
+ effects.chorus.separation_ms=3;
+ effects.chorus.depth_ms10=6,
+ effects.chorus.speed_hz10=5;
+ effects.reverb_mode=REVERB_MODE_ROOM;
+ }
+
+ if (p_clear_samples) {
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->reset();
+ }
+
+ if (p_clear_instruments) {
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->reset();
+ }
+
+ if (p_clear_patterns) {
+ for (int i=0;i<MAX_PATTERNS;i++)
+ get_pattern(i)->clear();
+
+ for (int i=0;i<MAX_ORDERS;i++)
+ set_order( i, CP_ORDER_NONE );
+ }
+
+
+}
+
+
+CPSong::ReverbMode CPSong::get_reverb_mode() {
+
+ return effects.reverb_mode;
+}
+void CPSong::set_reverb_mode(ReverbMode p_mode) {
+
+ effects.reverb_mode=p_mode;
+}
+
+void CPSong::set_chorus_delay_ms(int p_amount) {
+
+ effects.chorus.delay_ms=p_amount;
+}
+void CPSong::set_chorus_separation_ms(int p_amount) {
+
+ effects.chorus.separation_ms=p_amount;
+
+}
+void CPSong::set_chorus_depth_ms10(int p_amount) {
+
+ effects.chorus.depth_ms10=p_amount;
+
+}
+void CPSong::set_chorus_speed_hz10(int p_amount) {
+
+ effects.chorus.speed_hz10=p_amount;
+
+}
+
+int CPSong::get_chorus_delay_ms() {
+
+ return effects.chorus.delay_ms;
+
+}
+int CPSong::get_chorus_separation_ms() {
+
+ return effects.chorus.separation_ms;
+}
+int CPSong::get_chorus_depth_ms10() {
+
+ return effects.chorus.depth_ms10;
+
+}
+int CPSong::get_chorus_speed_hz10() {
+
+ return effects.chorus.speed_hz10;
+
+}
+
+void CPSong::cleanup_unused_patterns() {
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ bool used=false;
+ if (get_pattern(i)->is_empty())
+ continue;
+
+ for (int j=0;j<MAX_ORDERS;j++) {
+
+ if (get_order(j)==i) {
+ used=true;
+
+ }
+ }
+
+ if (!used)
+ get_pattern(i)->clear();
+ }
+
+}
+void CPSong::cleanup_unused_instruments(){
+
+ if (!has_instruments())
+ return;
+
+ bool instr_found[MAX_INSTRUMENTS];
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ instr_found[i]=false;
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ if (get_pattern(i)->is_empty())
+ continue;
+
+ for (int row=0;row<get_pattern(i)->get_length();row++) {
+
+
+ for (int col=0;col<CPPattern::WIDTH;col++) {
+
+ CPNote n;
+ n=get_pattern(i)->get_note( col,row );
+
+ if (n.instrument<MAX_INSTRUMENTS)
+ instr_found[n.instrument]=true;
+
+ }
+
+ }
+
+ }
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ if (!instr_found[i])
+ get_instrument(i)->reset();
+
+
+}
+void CPSong::cleanup_unused_samples(){
+
+ if (!has_instruments())
+ return;
+
+ bool sample_found[MAX_SAMPLES];
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ sample_found[i]=false;
+
+ for (int i=0;i<MAX_PATTERNS;i++) {
+
+ if (get_pattern(i)->is_empty())
+ continue;
+
+
+ for (int row=0;row<get_pattern(i)->get_length();row++) {
+
+
+ for (int col=0;col<CPPattern::WIDTH;col++) {
+
+ CPNote n;
+ n=get_pattern(i)->get_note( col,row );
+
+ if (n.instrument>=MAX_SAMPLES)
+ continue;
+
+ if (has_instruments()) {
+
+ for (int nt=0;nt<CPNote::NOTES;nt++) {
+
+ int smp=get_instrument(n.instrument)->get_sample_number(nt);
+ if (smp<MAX_SAMPLES)
+ sample_found[smp]=true;
+ }
+
+ } else {
+ if (n.instrument<MAX_SAMPLES)
+ sample_found[n.instrument]=true;
+ }
+
+ }
+
+ }
+
+ }
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ if (!sample_found[i])
+ get_sample(i)->reset();
+
+}
+void CPSong::cleanup_unused_orders(){
+
+ bool finito=false;
+ for (int j=0;j<MAX_ORDERS;j++) {
+
+
+ if (get_order(j)==CP_ORDER_NONE)
+ finito=true;
+ if (finito)
+ set_order(j,CP_ORDER_NONE);
+
+ }
+
+}
+
+void CPSong::clear_all_default_pan() {
+
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->set_pan_default_enabled( false ); //die!
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->set_pan_enabled( false ); //die!
+
+}
+
+
+void CPSong::clear_all_default_vol(){
+
+ for (int i=0;i<MAX_SAMPLES;i++)
+ get_sample(i)->set_default_volume( 64 ); //die!
+ for (int i=0;i<MAX_INSTRUMENTS;i++)
+ get_instrument(i)->set_volume_global_amount( CPInstrument::MAX_VOLUME );
+
+}
+
+
+int CPSong::get_order_in_use_count() {
+
+
+ int order_count = 0;
+
+ for (int i=(MAX_ORDERS-1);i>=0;i--) {
+
+
+ if (get_order(i)!=CP_ORDER_NONE) {
+ order_count=i+1;
+ break;
+ }
+ }
+
+ return order_count;
+}
+int CPSong::get_pattern_in_use_count() {
+
+
+ int pattern_count=0;
+
+ for (int i=(CPSong::MAX_PATTERNS-1);i>=0;i--) {
+
+
+ if (!get_pattern(i)->is_empty()) {
+ pattern_count=i+1;
+ break;
+ }
+ }
+
+ return pattern_count;
+}
+
+int CPSong::get_instrument_in_use_count() {
+
+ int instrument_count=0;
+
+ for (int i=(CPSong::MAX_INSTRUMENTS-1);i>=0;i--) {
+
+ CPInstrument *ins = get_instrument(i);
+ bool in_use=false;
+
+ for (int s = 0 ; s < CPNote::NOTES ; s++ ) {
+
+ int smp_idx = ins->get_sample_number(s);
+ if (smp_idx<0 || smp_idx>=CPSong::MAX_SAMPLES)
+ continue;
+
+ if (!get_sample(smp_idx)->get_sample_data().is_null()) {
+ in_use=true;
+ break;
+ }
+
+ }
+
+ if (in_use) {
+ instrument_count=i+1;
+ break;
+ }
+ }
+
+ return instrument_count;
+}
+#include <stdio.h>
+int CPSong::get_channels_in_use() {
+
+ int max=0;
+
+ for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
+
+ CPPattern *pat = get_pattern(p);
+ if (pat->is_empty())
+ continue;
+
+
+ for (int c=(CPPattern::WIDTH-1);c>=0;c--) {
+
+ if (c<max)
+ break;
+
+ bool has_note=false;
+ for (int r=0;r<pat->get_length();r++) {
+
+ CPNote n = pat->get_note( c, r );
+ if (!n.is_empty()) {
+ has_note=true;
+ break;
+ }
+ }
+
+ if (has_note) {
+
+ max=c+1;
+ }
+ }
+ }
+
+ return max;
+}
+
+
+void CPSong::separate_in_one_sample_instruments(int p_instrument) {
+
+ CP_ERR_COND( !variables.use_instruments );
+ CP_FAIL_INDEX( p_instrument, MAX_INSTRUMENTS );
+
+ int remapped_count=0;
+
+ signed char remap[MAX_SAMPLES];
+
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ remap[i]=-1;
+ }
+
+ /* Find remaps */
+ CPInstrument *ins=get_instrument(p_instrument);
+ for (int i=0;i<CPNote::NOTES;i++) {
+
+ int sn = ins->get_sample_number(i);
+
+ // check for unusable sample
+ if (sn<0 || sn>=MAX_SAMPLES || get_sample(sn)->get_sample_data().is_null())
+ continue;
+ printf("sample %i\n",sn);
+ if ( remap[sn] !=-1 ) {
+ printf("already mapped to %i\n",remap[sn]);
+ continue;
+ }
+
+ printf("isn't remapped\n");
+
+ // find remap
+
+ for (int j=0;j<MAX_INSTRUMENTS;j++) {
+
+ if (!get_instrument(j)->is_empty())
+ continue;
+
+ printf("map to %i\n",j);
+
+ //copy
+ *get_instrument(j)=*ins;
+
+ // assign samples
+ for (int k=0;k<CPNote::NOTES;k++) {
+
+ get_instrument(j)->set_note_number(k,k);
+ get_instrument(j)->set_sample_number(k,sn);
+ }
+ remap[sn]=j;
+ remapped_count++;
+ break;
+ }
+
+ CP_ERR_COND(remap[sn]==-1); // no more free instruments
+ }
+
+ printf("remapped %i\n",remapped_count);
+
+ if (remapped_count<2) {
+ //undo if only one is remapped
+ for (int i=0;i<MAX_SAMPLES;i++) {
+
+ if (remap[i]!=-1) {
+
+ get_instrument(remap[i])->reset();
+ }
+ }
+ return;
+ }
+
+ /* remap all song */
+
+ for (int p=0;p<CPSong::MAX_PATTERNS;p++) {
+
+ CPPattern *pat = get_pattern(p);
+ if (pat->is_empty())
+ continue;
+
+
+ for (int c=0;c<CPPattern::WIDTH;c++) {
+
+ for (int r=0;r<pat->get_length();r++) {
+
+ CPNote n = pat->get_note(c,r);
+ if (n.note<CPNote::NOTES && n.instrument==p_instrument) {
+
+ int sn = ins->get_sample_number(n.note);
+ if (remap[sn]==-1)
+ pat->set_note(c,r,CPNote());
+ else {
+
+ n.instrument=remap[sn];
+ pat->set_note(c,r,n);
+ }
+ }
+ }
+ }
+ }
+
+ ins->reset();
+
+}
+
+
+CPSong::CPSong() {
+
+ reset();
+}
+CPSong::~CPSong() {
+
+}
+
+
+
+
+int get_song_next_order_idx(CPSong *p_song, int p_order_idx) {
+
+ int baseorder,order_counter;
+
+ order_counter=-1;
+
+ baseorder=p_order_idx;
+
+ do {
+
+ baseorder++;
+ if ( baseorder>(CPSong::MAX_ORDERS-1) ) baseorder=0;
+ order_counter++;
+
+ } while ( (p_song->get_order(baseorder)>=(CPSong::MAX_PATTERNS) ) && (order_counter<CPSong::MAX_ORDERS) );
+
+
+ if (order_counter==CPSong::MAX_ORDERS) {
+
+ return -1;
+
+ } else {
+
+ return baseorder;
+ }
+
+}
diff --git a/modules/chibi/cp_song.h b/modules/chibi/cp_song.h
new file mode 100644
index 0000000000..da5d106a63
--- /dev/null
+++ b/modules/chibi/cp_song.h
@@ -0,0 +1,261 @@
+/*************************************************************************/
+/* cp_song.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CPSONG_H
+#define CPSONG_H
+
+
+/**CPSong Class
+ *@author Juan Linietsky
+ */
+
+#include "cp_order.h"
+#include "cp_pattern.h"
+#include "cp_sample.h"
+#include "cp_instrument.h"
+
+class CPSong {
+public:
+ enum {
+ MAX_SONG_NAME=26,
+ MAX_ORDERS=200,
+ MAX_PATTERNS=200,
+ MAX_SAMPLES=99,
+ MAX_INSTRUMENTS=99,
+
+ CHANNEL_MAX_PAN=64,
+ CHANNEL_MAX_VOLUME=64,
+ CHANNEL_MAX_CHORUS=64,
+ CHANNEL_MAX_REVERB=64,
+
+ MIN_TEMPO=31,
+ MAX_TEMPO=255,
+ MIN_SPEED=1,
+ MAX_SPEED=255,
+ MAX_MESSAGE_LEN=8000,
+
+
+
+ };
+
+ enum ReverbMode {
+ REVERB_MODE_ROOM,
+ REVERB_MODE_STUDIO_SMALL,
+ REVERB_MODE_STUDIO_MEDIUM,
+ REVERB_MODE_STUDIO_LARGE,
+ REVERB_MODE_HALL,
+ REVERB_MODE_SPACE_ECHO,
+ REVERB_MODE_ECHO,
+ REVERB_MODE_DELAY,
+ REVERB_MODE_HALF_ECHO
+ };
+
+private:
+ CPOrder order[MAX_ORDERS];
+ CPPattern pattern[MAX_PATTERNS];
+ CPSample sample[MAX_SAMPLES];
+ CPInstrument instrument[MAX_INSTRUMENTS];
+
+
+
+ struct Song_Variables { // variables that wont change in playback
+
+ char name[MAX_SONG_NAME];
+ char message[MAX_MESSAGE_LEN];
+ /* string message; */
+
+ int row_highlight_minor;
+ int row_highlight_major;
+
+ int mixing_volume;
+ int stereo_separation;
+
+ bool use_stereo;
+ bool use_instruments;
+ bool use_linear_slides;
+
+ bool old_effects;
+ bool compatible_gxx;
+
+ } variables;
+
+ struct Initial_Variables { // Initial values used for playback
+
+ struct Channel_State {
+
+ int pan,volume; // 0-- CHANNEL_MAX_PAN, CHANNEL_MAX_VOLUME
+ bool surround;
+ bool mute;
+ int chorus; //0 - 64
+ int reverb; //0 - 64
+ };
+
+ int global_volume;
+ int speed;
+ int tempo;
+
+ Channel_State channel[CPPattern::WIDTH];
+ } initial_variables;
+
+ struct Effects {
+
+ ReverbMode reverb_mode;
+
+ struct Chorus {
+
+ int delay_ms;
+ int separation_ms;
+ int depth_ms10;
+ int speed_hz10;
+ } chorus;
+
+ } effects;
+
+public:
+
+ /* Properties */
+
+ const char *get_name();
+ void set_name(const char *p_name);
+
+ const char *get_message();
+ void set_message(const char *p_message);
+
+ void set_row_highlight_minor(int p_hl_minor); /* 0 .. 256 */
+ int get_row_highlight_minor(); /* 0 .. 256 */
+
+ void set_row_highlight_major(int p_hl_major); /* 0 .. 256 */
+ int get_row_highlight_major(); /* 0 .. 256 */
+
+ void set_mixing_volume(int p_mix_volume); /* 0 .. 128 */
+ int get_mixing_volume(); /* 0 .. 128 */
+
+ void set_global_volume(int p_global_volume); /* 0 .. 128 */
+ int get_global_volume(); /* 0 .. 128 */
+
+ void set_stereo_separation(int p_separation); /* 0 .. 128 */
+ int get_stereo_separation(); /* 0 .. 128 */
+
+ void set_stereo(bool p_stereo);
+ bool is_stereo();
+
+ void set_instruments(bool p_instruments);
+ bool has_instruments();
+
+ void set_linear_slides(bool p_linear_slides);
+ bool has_linear_slides();
+
+ void set_old_effects(bool p_old_effects);
+ bool has_old_effects();
+
+ void set_compatible_gxx(bool p_compatible_gxx);
+ bool has_compatible_gxx();
+
+ void set_speed(int p_speed); /* 1 .. 255 */
+ int get_speed(); /* 1 .. 255 */
+
+ void set_tempo(int p_tempo); /* 31 .. 255 */
+ int get_tempo(); /* 31 .. 255 */
+
+ void set_channel_pan(int p_channel,int p_pan); /* 0 .. 64 */
+ int get_channel_pan(int p_channel);
+
+ void set_channel_volume(int p_channel,int p_volume); /* 0 .. 64 */
+ int get_channel_volume(int p_channel);
+
+ void set_channel_surround(int p_channel,bool p_surround);
+ bool is_channel_surround(int p_channel);
+
+ void set_channel_mute(int p_channel,bool p_mute);
+ bool is_channel_mute(int p_channel);
+
+ void set_channel_chorus(int p_channel,int p_chorus); /* 0 .. 64 */
+ int get_channel_chorus(int p_channel);
+
+ void set_channel_reverb(int p_channel,int p_reverb); /* 0 .. 64 */
+ int get_channel_reverb(int p_channel);
+
+ /* arrays of stuff */
+
+ CPPattern* get_pattern(int p_pattern);
+ CPSample* get_sample(int p_sample);
+ CPInstrument* get_instrument(int p_instrument);
+
+ int get_order(int p_position);
+ void set_order(int p_position,int p_order);
+
+
+ /* Effects */
+
+ ReverbMode get_reverb_mode();
+ void set_reverb_mode(ReverbMode p_mode);
+
+ void set_chorus_delay_ms(int p_amount);
+ void set_chorus_separation_ms(int p_amount);
+ void set_chorus_depth_ms10(int p_amount);
+ void set_chorus_speed_hz10(int p_amount);
+
+ int get_chorus_delay_ms();
+ int get_chorus_separation_ms();
+ int get_chorus_depth_ms10();
+ int get_chorus_speed_hz10();
+
+ /* utils */
+
+ void reset(bool p_clear_patterns=true,bool p_clear_samples=true,bool p_clear_instruments=true,bool p_clear_variables=true);
+
+ void cleanup_unused_patterns();
+ void cleanup_unused_instruments();
+ void cleanup_unused_samples();
+ void cleanup_unused_orders();
+ void clear_all_default_pan();
+ void clear_all_default_vol();
+
+ void clear_instrument_with_samples(int p_instrument);
+
+ void make_instruments_from_samples();
+ void make_instrument_from_sample(int p_sample);
+
+ void separate_in_one_sample_instruments(int p_instrument);
+
+ int get_order_in_use_count();
+ int get_pattern_in_use_count();
+ int get_instrument_in_use_count();
+ int get_channels_in_use();
+
+ CPSong();
+ ~CPSong();
+
+};
+
+
+/* Some helper for something used a lot */
+
+int get_song_next_order_idx(CPSong *p_song, int p_order_idx);
+
+#endif
diff --git a/modules/chibi/cp_tables.cpp b/modules/chibi/cp_tables.cpp
new file mode 100644
index 0000000000..8c62150f31
--- /dev/null
+++ b/modules/chibi/cp_tables.cpp
@@ -0,0 +1,254 @@
+/*************************************************************************/
+/* cp_tables.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "cp_tables.h"
+
+int32_t CPTables::linear_period_to_freq_tab[768]={
+
+ 535232,534749,534266,533784,533303,532822,532341,531861,
+ 531381,530902,530423,529944,529466,528988,528511,528034,
+ 527558,527082,526607,526131,525657,525183,524709,524236,
+ 523763,523290,522818,522346,521875,521404,520934,520464,
+ 519994,519525,519057,518588,518121,517653,517186,516720,
+ 516253,515788,515322,514858,514393,513929,513465,513002,
+ 512539,512077,511615,511154,510692,510232,509771,509312,
+ 508852,508393,507934,507476,507018,506561,506104,505647,
+ 505191,504735,504280,503825,503371,502917,502463,502010,
+ 501557,501104,500652,500201,499749,499298,498848,498398,
+ 497948,497499,497050,496602,496154,495706,495259,494812,
+ 494366,493920,493474,493029,492585,492140,491696,491253,
+ 490809,490367,489924,489482,489041,488600,488159,487718,
+ 487278,486839,486400,485961,485522,485084,484647,484210,
+ 483773,483336,482900,482465,482029,481595,481160,480726,
+ 480292,479859,479426,478994,478562,478130,477699,477268,
+ 476837,476407,475977,475548,475119,474690,474262,473834,
+ 473407,472979,472553,472126,471701,471275,470850,470425,
+ 470001,469577,469153,468730,468307,467884,467462,467041,
+ 466619,466198,465778,465358,464938,464518,464099,463681,
+ 463262,462844,462427,462010,461593,461177,460760,460345,
+ 459930,459515,459100,458686,458272,457859,457446,457033,
+ 456621,456209,455797,455386,454975,454565,454155,453745,
+ 453336,452927,452518,452110,451702,451294,450887,450481,
+ 450074,449668,449262,448857,448452,448048,447644,447240,
+ 446836,446433,446030,445628,445226,444824,444423,444022,
+ 443622,443221,442821,442422,442023,441624,441226,440828,
+ 440430,440033,439636,439239,438843,438447,438051,437656,
+ 437261,436867,436473,436079,435686,435293,434900,434508,
+ 434116,433724,433333,432942,432551,432161,431771,431382,
+ 430992,430604,430215,429827,429439,429052,428665,428278,
+ 427892,427506,427120,426735,426350,425965,425581,425197,
+ 424813,424430,424047,423665,423283,422901,422519,422138,
+ 421757,421377,420997,420617,420237,419858,419479,419101,
+ 418723,418345,417968,417591,417214,416838,416462,416086,
+ 415711,415336,414961,414586,414212,413839,413465,413092,
+ 412720,412347,411975,411604,411232,410862,410491,410121,
+ 409751,409381,409012,408643,408274,407906,407538,407170,
+ 406803,406436,406069,405703,405337,404971,404606,404241,
+ 403876,403512,403148,402784,402421,402058,401695,401333,
+ 400970,400609,400247,399886,399525,399165,398805,398445,
+ 398086,397727,397368,397009,396651,396293,395936,395579,
+ 395222,394865,394509,394153,393798,393442,393087,392733,
+ 392378,392024,391671,391317,390964,390612,390259,389907,
+ 389556,389204,388853,388502,388152,387802,387452,387102,
+ 386753,386404,386056,385707,385359,385012,384664,384317,
+ 383971,383624,383278,382932,382587,382242,381897,381552,
+ 381208,380864,380521,380177,379834,379492,379149,378807,
+ 378466,378124,377783,377442,377102,376762,376422,376082,
+ 375743,375404,375065,374727,374389,374051,373714,373377,
+ 373040,372703,372367,372031,371695,371360,371025,370690,
+ 370356,370022,369688,369355,369021,368688,368356,368023,
+ 367691,367360,367028,366697,366366,366036,365706,365376,
+ 365046,364717,364388,364059,363731,363403,363075,362747,
+ 362420,362093,361766,361440,361114,360788,360463,360137,
+ 359813,359488,359164,358840,358516,358193,357869,357547,
+ 357224,356902,356580,356258,355937,355616,355295,354974,
+ 354654,354334,354014,353695,353376,353057,352739,352420,
+ 352103,351785,351468,351150,350834,350517,350201,349885,
+ 349569,349254,348939,348624,348310,347995,347682,347368,
+ 347055,346741,346429,346116,345804,345492,345180,344869,
+ 344558,344247,343936,343626,343316,343006,342697,342388,
+ 342079,341770,341462,341154,340846,340539,340231,339924,
+ 339618,339311,339005,338700,338394,338089,337784,337479,
+ 337175,336870,336566,336263,335959,335656,335354,335051,
+ 334749,334447,334145,333844,333542,333242,332941,332641,
+ 332341,332041,331741,331442,331143,330844,330546,330247,
+ 329950,329652,329355,329057,328761,328464,328168,327872,
+ 327576,327280,326985,326690,326395,326101,325807,325513,
+ 325219,324926,324633,324340,324047,323755,323463,323171,
+ 322879,322588,322297,322006,321716,321426,321136,320846,
+ 320557,320267,319978,319690,319401,319113,318825,318538,
+ 318250,317963,317676,317390,317103,316817,316532,316246,
+ 315961,315676,315391,315106,314822,314538,314254,313971,
+ 313688,313405,313122,312839,312557,312275,311994,311712,
+ 311431,311150,310869,310589,310309,310029,309749,309470,
+ 309190,308911,308633,308354,308076,307798,307521,307243,
+ 306966,306689,306412,306136,305860,305584,305308,305033,
+ 304758,304483,304208,303934,303659,303385,303112,302838,
+ 302565,302292,302019,301747,301475,301203,300931,300660,
+ 300388,300117,299847,299576,299306,299036,298766,298497,
+ 298227,297958,297689,297421,297153,296884,296617,296349,
+ 296082,295815,295548,295281,295015,294749,294483,294217,
+ 293952,293686,293421,293157,292892,292628,292364,292100,
+ 291837,291574,291311,291048,290785,290523,290261,289999,
+ 289737,289476,289215,288954,288693,288433,288173,287913,
+ 287653,287393,287134,286875,286616,286358,286099,285841,
+ 285583,285326,285068,284811,284554,284298,284041,283785,
+ 283529,283273,283017,282762,282507,282252,281998,281743,
+ 281489,281235,280981,280728,280475,280222,279969,279716,
+ 279464,279212,278960,278708,278457,278206,277955,277704,
+ 277453,277203,276953,276703,276453,276204,275955,275706,
+ 275457,275209,274960,274712,274465,274217,273970,273722,
+ 273476,273229,272982,272736,272490,272244,271999,271753,
+ 271508,271263,271018,270774,270530,270286,270042,269798,
+ 269555,269312,269069,268826,268583,268341,268099,267857
+};
+
+uint16_t CPTables::old_period_table[OCTAVE*2]={
+
+ 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
+ 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
+ 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160,
+ 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
+};
+
+#define LOGFAC 2*16
+
+uint16_t CPTables::log_table[104]= {
+ LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,
+ LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
+ LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,
+ LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
+ LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,
+ LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
+ LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,
+ LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
+ LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,
+ LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
+ LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,
+ LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
+ LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,
+ LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
+ LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,
+ LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
+ LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,
+ LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
+ LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,
+ LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
+ LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,
+ LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
+ LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,
+ LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
+ LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,
+ LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
+};
+
+
+
+int32_t CPTables::get_linear_period(uint16_t note,int32_t fine) {
+
+ int32_t t;
+
+ t=(24L*OCTAVE-(int32_t)note)*32L-(fine>>1);
+ return t;
+}
+
+static int s3m_period_table[12]={1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907};
+
+
+int32_t CPTables::get_log_period(uint16_t note,int32_t p_c5freq) {
+
+ return (8363L * 16 * s3m_period_table[note%12] >> (note/12)) / p_c5freq;
+
+}
+
+/*
+int32_t CPTables::get_log_period(uint16_t note,int32_t p_c5freq)
+{
+ uint16_t n,o;
+ uint16_t p1,p2;
+ int32_t i;
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+ i=(n<<2); // n*8 + fine/16
+
+ if (i<0)
+ i=0;
+
+ if (i>102)
+ i=102;
+
+
+ p1=log_table[i];
+ p2=log_table[i+1];
+
+
+ return (Interpolate(fine>>4,0,15,p1,p2)>>o);
+
+} */
+
+int32_t CPTables::get_old_period(uint16_t note,int32_t speed) {
+
+ uint16_t n,o,res;
+
+// if (!speed) {
+
+ // return 4242; /* <- prevent divide overflow */
+ // }
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+
+ res=((8363L*(int32_t)old_period_table[n])>>o)/((old_period_table[17]>>1)+(speed<<2)); /*/(128-speed)*/;
+
+ return res;
+}
+
+int32_t CPTables::get_linear_frequency(int32_t period) {
+
+ int32_t shift_value=(((int32_t)period/768)-2);
+ if (shift_value>0) {
+
+ return linear_period_to_freq_tab[period%768]>>shift_value;
+ } else {
+ shift_value=0-shift_value;
+ return linear_period_to_freq_tab[period%768]<<shift_value;
+ }
+}
+
+int32_t CPTables::get_old_frequency(int32_t period) {
+
+ return (8363L*1712L)/(period?period:1);
+
+}
+
+CPTables::CPTables(){
+}
+CPTables::~CPTables(){
+}
diff --git a/modules/chibi/cp_tables.h b/modules/chibi/cp_tables.h
new file mode 100644
index 0000000000..ac7ee562b7
--- /dev/null
+++ b/modules/chibi/cp_tables.h
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* cp_tables.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CPTABLES_H
+#define CPTABLES_H
+
+#include "cp_config.h"
+
+/**conversion CPTables/functions
+ *@author Juan Linietsky
+ */
+
+/******************************
+ CPTables.h
+ --------
+
+CPTables methods for miscelaneous
+conversion utilities
+********************************/
+
+class CPTables {
+public:
+
+ enum { OCTAVE=12 };
+
+ static uint16_t old_period_table[OCTAVE*2];
+ static uint16_t log_table[104];
+ static int32_t linear_period_to_freq_tab[768];
+
+ static int32_t get_old_period(uint16_t note,int32_t speed);
+ static int32_t get_amiga_period(uint16_t note,int32_t fine);
+ static int32_t get_linear_period(uint16_t note,int32_t fine);
+ static int32_t get_linear_frequency(int32_t period);
+ static int32_t get_old_frequency(int32_t period);
+ static int32_t get_log_period(uint16_t note,int32_t p_c5freq);
+
+ CPTables();
+ ~CPTables();
+};
+
+#endif
diff --git a/modules/chibi/event_stream_chibi.cpp b/modules/chibi/event_stream_chibi.cpp
new file mode 100644
index 0000000000..b88f4ee70e
--- /dev/null
+++ b/modules/chibi/event_stream_chibi.cpp
@@ -0,0 +1,872 @@
+/*************************************************************************/
+/* event_stream_chibi.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "event_stream_chibi.h"
+#include "cp_loader_it.h"
+#include "cp_loader_xm.h"
+#include "cp_loader_s3m.h"
+#include "cp_loader_mod.h"
+
+static CPSampleManagerImpl *sample_manager;
+static ResourceFormatLoaderChibi *resource_loader;
+
+CPSample_ID CPSampleManagerImpl::create(bool p_16bits,bool p_stereo,int32_t p_len) {
+
+ AudioServer::SampleFormat sf=p_16bits?AudioServer::SAMPLE_FORMAT_PCM16:AudioServer::SAMPLE_FORMAT_PCM8;
+
+ SampleData *sd = memnew( SampleData );
+ sd->rid = AudioServer::get_singleton()->sample_create(sf,p_stereo,p_len);
+ sd->stereo=p_stereo;
+ sd->len=p_len;
+ sd->is16=p_16bits;
+ sd->mixfreq=44100;
+ sd->loop_begin=0;
+ sd->loop_end=0;
+ sd->loop_type=CP_LOOP_NONE;
+ sd->locks=0;
+#ifdef DEBUG_ENABLED
+ valid.insert(sd);
+#endif
+ CPSample_ID sid;
+ sid._private=sd;
+ return sid;
+}
+
+void CPSampleManagerImpl::recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len){
+
+ AudioServer::SampleFormat sf=p_16bits?AudioServer::SAMPLE_FORMAT_PCM16:AudioServer::SAMPLE_FORMAT_PCM8;
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ AudioServer::get_singleton()->free(sd->rid);
+ sd->rid = AudioServer::get_singleton()->sample_create(sf,p_stereo,p_len);
+ sd->stereo=p_stereo;
+ sd->len=p_len;
+ sd->is16=p_16bits;
+ sd->mixfreq=44100;
+ sd->loop_begin=0;
+ sd->loop_end=0;
+ sd->loop_type=CP_LOOP_NONE;
+}
+void CPSampleManagerImpl::destroy(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+ valid.erase(sd);
+#endif
+ AudioServer::get_singleton()->free(sd->rid);
+
+ memdelete(sd);
+}
+bool CPSampleManagerImpl::check(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ return valid.has(sd);
+#else
+ return _getsd(p_id)!=NULL;
+#endif
+}
+
+void CPSampleManagerImpl::set_c5_freq(CPSample_ID p_id,int32_t p_freq){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->mixfreq=p_freq;
+ AudioServer::get_singleton()->sample_set_mix_rate(sd->rid,p_freq);
+
+}
+void CPSampleManagerImpl::set_loop_begin(CPSample_ID p_id,int32_t p_begin){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->loop_begin=p_begin;
+ AudioServer::get_singleton()->sample_set_loop_begin(sd->rid,p_begin);
+
+}
+void CPSampleManagerImpl::set_loop_end(CPSample_ID p_id,int32_t p_end){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+ sd->loop_end=p_end;
+ AudioServer::get_singleton()->sample_set_loop_end(sd->rid,p_end);
+
+}
+void CPSampleManagerImpl::set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ sd->loop_type=p_type;
+ AudioServer::get_singleton()->sample_set_loop_format(sd->rid,AudioServer::SampleLoopFormat(p_type));
+
+
+}
+void CPSampleManagerImpl::set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+}
+
+
+int32_t CPSampleManagerImpl::get_loop_begin(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->loop_begin;
+
+}
+int32_t CPSampleManagerImpl::get_loop_end(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->loop_end;
+}
+CPSample_Loop_Type CPSampleManagerImpl::get_loop_type(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),CP_LOOP_NONE);
+#endif
+
+ return sd->loop_type;
+}
+int32_t CPSampleManagerImpl::get_c5_freq(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->mixfreq;
+}
+int32_t CPSampleManagerImpl::get_size(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ return sd->len;
+
+}
+bool CPSampleManagerImpl::is_16bits(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),false);
+#endif
+
+ return sd->is16;
+
+}
+bool CPSampleManagerImpl::is_stereo(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),false);
+#endif
+ return sd->stereo;
+
+
+}
+bool CPSampleManagerImpl::lock_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ sd->locks++;
+ if (sd->locks==1) {
+ sd->lock=AudioServer::get_singleton()->sample_get_data(sd->rid);
+ sd->w=sd->lock.write();
+ }
+
+ return true;
+}
+void *CPSampleManagerImpl::get_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ ERR_FAIL_COND_V(sd->locks==0,0);
+ return sd->w.ptr();
+}
+
+int16_t CPSampleManagerImpl::get_data(CPSample_ID p_id, int p_sample, int p_channel){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V(!valid.has(sd),0);
+#endif
+
+ ERR_FAIL_V(0);
+ lock_data(p_id);
+
+ int sofs = sd->stereo ? 2:1;
+ uint16_t v=0;
+ if (sd->is16) {
+ int16_t *p=(int16_t*)sd->w.ptr();
+ v=p[p_sample*sofs+p_channel];
+ } else {
+ int8_t *p=(int8_t*)sd->w.ptr();
+ v=p[p_sample*sofs+p_channel];
+ }
+
+ unlock_data(p_id);
+
+ return v;
+}
+void CPSampleManagerImpl::set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+ lock_data(p_id);
+
+ int sofs = sd->stereo ? 2:1;
+ if (sd->is16) {
+ int16_t *p=(int16_t*)sd->w.ptr();
+ p[p_sample*sofs+p_channel]=p_data;
+ } else {
+ int8_t *p=(int8_t*)sd->w.ptr();
+ p[p_sample*sofs+p_channel]=p_data;
+ }
+
+ unlock_data(p_id);
+
+}
+void CPSampleManagerImpl::unlock_data(CPSample_ID p_id){
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL_COND(sd->locks==0);
+
+ sd->locks--;
+ if (sd->locks==0) {
+ sd->w=DVector<uint8_t>::Write();
+ AudioServer::get_singleton()->sample_set_data(sd->rid,sd->lock);
+ sd->lock=DVector<uint8_t>();
+ }
+}
+
+void CPSampleManagerImpl::get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len) {
+
+ SampleData *sd=_getsd(p_id);
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(!valid.has(sd));
+#endif
+
+ ERR_FAIL();
+}
+
+
+/** MIXER **/
+
+void CPMixerImpl::set_callback_interval(int p_interval_us) {
+
+ callback_interval=p_interval_us;
+}
+
+void CPMixerImpl::set_callback(void (*p_callback)(void*),void *p_userdata) {
+
+ callback=p_callback;
+ userdata=p_userdata;
+}
+
+void CPMixerImpl::setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) {
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel!=AudioMixer::INVALID_CHANNEL) {
+ mixer->channel_free(v.channel);
+ }
+ v.channel=mixer->channel_alloc(sample_manager->get_rid(p_sample_id));
+ v.freq_mult = sample_manager->get_c5_freq(p_sample_id)/261.6255653006;
+ v.sample = p_sample_id;
+}
+
+void CPMixerImpl::stop_voice(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel==AudioMixer::INVALID_CHANNEL)
+ return;
+
+ mixer->channel_free(v.channel);
+ v.channel=AudioMixer::INVALID_CHANNEL;
+
+}
+
+void CPMixerImpl::set_voice_frequency(int p_voice_index,int32_t p_freq) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ float f = p_freq / 256.0;
+ f*=pitch_scale;
+ mixer->channel_set_mix_rate(v.channel,f * v.freq_mult );
+}
+
+void CPMixerImpl::set_voice_panning(int p_voice_index,int p_pan) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ if (p_pan==CP_PAN_SURROUND)
+ p_pan=CP_PAN_CENTER;
+ float p = p_pan / 256.0;
+ mixer->channel_set_pan(v.channel,p);
+
+}
+
+void CPMixerImpl::set_voice_volume(int p_voice_index,int p_vol) {
+
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ float vol = p_vol/512.0;
+ vol*=voice_scale;
+ mixer->channel_set_volume(v.channel,vol);
+ mixer->channel_set_reverb(v.channel,reverb_type,vol*v.reverb);
+}
+
+void CPMixerImpl::set_voice_filter(int p_voice_index,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance ){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+
+}
+
+void CPMixerImpl::set_voice_reverb_send(int p_voice_index,int p_reverb){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ v.reverb=p_reverb/255.0;
+ //mixer->channel_set_reverb(v.channel,reverb_type,p_reverb/255.0);
+
+}
+
+void CPMixerImpl::set_voice_chorus_send(int p_voice_index,int p_chorus){
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ mixer->channel_set_chorus(v.channel,p_chorus/255.0);
+
+}
+
+
+void CPMixerImpl::set_reverb_mode(ReverbMode p_mode){
+
+// Voice &v=voices[p_voice_index];
+// ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+ switch(p_mode) {
+ case CPMixer::REVERB_MODE_STUDIO_SMALL: reverb_type=AudioMixer::REVERB_SMALL; break;
+ case CPMixer::REVERB_MODE_STUDIO_MEDIUM: reverb_type=AudioMixer::REVERB_MEDIUM; break;
+ case CPMixer::REVERB_MODE_STUDIO_LARGE: reverb_type=AudioMixer::REVERB_LARGE; break;
+ case CPMixer::REVERB_MODE_HALL: reverb_type=AudioMixer::REVERB_HALL; break;
+ default: reverb_type=AudioMixer::REVERB_SMALL; break;
+ }
+
+}
+
+void CPMixerImpl::set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10){
+
+// Voice &v=voices[p_voice_index];
+// ERR_FAIL_COND(v.channel==AudioMixer::INVALID_CHANNEL);
+
+}
+
+
+
+/* Info retrieving */
+
+int32_t CPMixerImpl::get_voice_sample_pos_index(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(v.channel==AudioMixer::INVALID_CHANNEL,0);
+ return 0;
+
+}
+
+int CPMixerImpl::get_voice_panning(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(!is_voice_active(p_voice_index),0);
+ return mixer->channel_get_pan(v.channel)*CP_PAN_RIGHT;
+
+}
+
+int CPMixerImpl::get_voice_volume(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(!is_voice_active(p_voice_index),0);
+ return mixer->channel_get_volume(v.channel);
+
+
+}
+
+CPSample_ID CPMixerImpl::get_voice_sample_id(int p_voice_index) {
+
+ Voice &v=voices[p_voice_index];
+ ERR_FAIL_COND_V(v.channel==AudioMixer::INVALID_CHANNEL,CPSample_ID());
+ return v.sample;
+
+
+}
+
+bool CPMixerImpl::is_voice_active(int p_voice_index){
+
+ Voice &v=voices[p_voice_index];
+ if (v.channel==AudioMixer::INVALID_CHANNEL)
+ return false;
+ if (!mixer->channel_is_valid(v.channel))
+ v.channel=AudioMixer::INVALID_CHANNEL;
+
+ return v.channel!=AudioMixer::INVALID_CHANNEL;
+}
+
+void CPMixerImpl::process_usecs(int p_usec,float p_volume,float p_pitch_scale,float p_tempo_scale) {
+
+ ERR_FAIL_COND(callback_interval==0);
+ //update this somewhere
+ pitch_scale=p_pitch_scale;
+ tempo_scale=p_tempo_scale;
+ voice_scale = AudioServer::get_singleton()->get_event_voice_global_volume_scale()*p_volume;
+ while(p_usec) {
+
+ if (p_usec>=callback_timeout) {
+
+ p_usec-=callback_timeout;
+ callback_timeout=0;
+ if (callback) {
+ callback(userdata);
+ }
+ callback_timeout=callback_interval*(1.0/p_tempo_scale);
+
+ } else {
+
+ callback_timeout-=p_usec;
+ p_usec=0;
+ }
+ }
+}
+
+
+CPMixerImpl::CPMixerImpl(AudioMixer *p_mixer) {
+
+ callback_interval=1;
+ callback_timeout=0;
+ userdata=0;
+ callback=0;
+ tempo_scale=1.0;
+ pitch_scale=1.0;
+ mixer=p_mixer;
+ voice_scale = AudioServer::get_singleton()->get_event_voice_global_volume_scale();
+ reverb_type = AudioMixer::REVERB_SMALL;
+
+}
+
+/** FILE ACCESS WRAPPER **/
+
+
+CPFileAccessWrapperImpl::Error CPFileAccessWrapperImpl::open(const char *p_filename, int p_mode_flags) {
+
+ ERR_FAIL_COND_V(p_mode_flags&WRITE,ERROR_WRITING_FILE);
+ close();
+ f = FileAccess::open(String::utf8(p_filename),p_mode_flags);
+ if (!f)
+ return ERROR_FILE_NOT_FOUND;
+ return OK;
+}
+
+void CPFileAccessWrapperImpl::close(){
+
+ if (f)
+ memdelete(f);
+ f=NULL;
+
+
+}
+
+void CPFileAccessWrapperImpl::seek(uint32_t p_position){
+
+ f->seek(p_position);
+}
+void CPFileAccessWrapperImpl::seek_end(){
+
+ f->seek_end();
+}
+uint32_t CPFileAccessWrapperImpl::get_pos(){
+
+ return f->get_pos();
+}
+
+bool CPFileAccessWrapperImpl::eof_reached(){
+
+ return f->eof_reached();
+}
+
+uint8_t CPFileAccessWrapperImpl::get_byte(){
+
+ return f->get_8();
+}
+void CPFileAccessWrapperImpl::get_byte_array(uint8_t *p_dest,int p_elements){
+
+ f->get_buffer(p_dest,p_elements);
+}
+void CPFileAccessWrapperImpl::get_word_array(uint16_t *p_dest,int p_elements){
+
+ for(int i=0;i<p_elements;i++) {
+ p_dest[i]=f->get_16();
+ }
+
+}
+
+uint16_t CPFileAccessWrapperImpl::get_word(){
+
+ return f->get_16();
+}
+uint32_t CPFileAccessWrapperImpl::get_dword(){
+
+ return f->get_32();
+}
+
+void CPFileAccessWrapperImpl::set_endian_conversion(bool p_swap){
+
+ f->set_endian_swap(p_swap);
+}
+bool CPFileAccessWrapperImpl::is_open(){
+
+ return f!=NULL;
+}
+
+CPFileAccessWrapperImpl::Error CPFileAccessWrapperImpl::get_error(){
+
+ return (f->get_error()!=::OK)?ERROR_READING_FILE:OK;
+}
+
+void CPFileAccessWrapperImpl::store_byte(uint8_t p_dest){
+
+}
+void CPFileAccessWrapperImpl::store_byte_array(const uint8_t *p_dest,int p_elements){
+
+}
+
+void CPFileAccessWrapperImpl::store_word(uint16_t p_dest){
+
+}
+void CPFileAccessWrapperImpl::store_dword(uint32_t p_dest){
+
+}
+
+////////////////////////////////////////////////
+
+
+Error EventStreamPlaybackChibi::_play() {
+
+ last_order=0;
+ loops=0;
+ player->play_start_song();
+ total_usec=0;
+
+ return OK;
+}
+
+bool EventStreamPlaybackChibi::_update(AudioMixer* p_mixer, uint64_t p_usec){
+
+ total_usec+=p_usec;
+ mixer.process_usecs(p_usec,volume,pitch_scale,tempo_scale);
+ int order=player->get_current_order();
+ if (order<last_order) {
+ if (!loop) {
+ stop();
+ } else {
+ loops++;
+ }
+ }
+ last_order=order;
+ return false;
+}
+void EventStreamPlaybackChibi::_stop(){
+
+ player->play_stop();
+}
+
+void EventStreamPlaybackChibi::set_paused(bool p_paused){
+
+}
+bool EventStreamPlaybackChibi::is_paused() const{
+
+ return false;
+}
+void EventStreamPlaybackChibi::set_loop(bool p_loop){
+
+ loop=p_loop;
+
+}
+bool EventStreamPlaybackChibi::is_loop_enabled() const{
+
+ return loop;
+}
+
+int EventStreamPlaybackChibi::get_loop_count() const{
+
+ //return player->is
+ return loops;
+}
+
+float EventStreamPlaybackChibi::get_pos() const{
+
+ return double(total_usec)/1000000.0;
+}
+void EventStreamPlaybackChibi::seek_pos(float p_time){
+
+ WARN_PRINT("seek_pos unimplemented.");
+}
+
+void EventStreamPlaybackChibi::set_volume(float p_volume) {
+
+ volume=p_volume;
+}
+
+float EventStreamPlaybackChibi::get_volume() const{
+
+ return volume;
+}
+
+void EventStreamPlaybackChibi::set_pitch_scale(float p_pitch_scale) {
+
+ pitch_scale=p_pitch_scale;
+}
+
+float EventStreamPlaybackChibi::get_pitch_scale() const{
+
+ return pitch_scale;
+}
+
+void EventStreamPlaybackChibi::set_tempo_scale(float p_tempo_scale) {
+
+ tempo_scale=p_tempo_scale;
+}
+
+float EventStreamPlaybackChibi::get_tempo_scale() const{
+
+ return tempo_scale;
+}
+
+
+void EventStreamPlaybackChibi::set_channel_volume(int p_channel,float p_volume) {
+
+
+ if (p_channel>=64)
+ return;
+ player->set_channel_global_volume(p_channel,p_volume*256);
+}
+
+
+
+float EventStreamPlaybackChibi::get_channel_volume(int p_channel) const{
+
+ return player->get_channel_global_volume(p_channel)/256.0;
+
+}
+
+float EventStreamPlaybackChibi::get_last_note_time(int p_channel) const {
+
+
+ double v = (player->get_channel_last_note_time_usec(p_channel))/1000000.0;
+ if (v<0)
+ v=-1;
+ return v;
+}
+
+EventStreamPlaybackChibi::EventStreamPlaybackChibi(Ref<EventStreamChibi> p_stream) : mixer(_get_mixer()) {
+
+ stream=p_stream;
+ player = memnew( CPPlayer(&mixer,&p_stream->song) );
+ loop=false;
+ last_order=0;
+ loops=0;
+ volume=1.0;
+ pitch_scale=1.0;
+ tempo_scale=1.0;
+}
+EventStreamPlaybackChibi::~EventStreamPlaybackChibi(){
+
+ player->play_stop();
+ memdelete(player);
+}
+
+////////////////////////////////////////////////////
+
+Ref<EventStreamPlayback> EventStreamChibi::instance_playback() {
+
+ return Ref<EventStreamPlayback>( memnew(EventStreamPlaybackChibi(Ref<EventStreamChibi>(this))) );
+}
+
+String EventStreamChibi::get_stream_name() const{
+
+ return song.get_name();
+
+}
+
+
+
+float EventStreamChibi::get_length() const{
+
+ return 1;
+}
+
+
+EventStreamChibi::EventStreamChibi() {
+
+
+}
+
+
+
+//////////////////////////////////////////////////////////////////
+
+
+
+
+RES ResourceFormatLoaderChibi::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+ String el = p_path.extension().to_lower();
+
+ CPFileAccessWrapperImpl f;
+
+ if (el=="it") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_IT loader(&f);
+ CPLoader::Error err = loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+
+ return esc;
+
+ } else if (el=="xm") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_XM loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+ return esc;
+
+ } else if (el=="s3m") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_S3M loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+
+ return esc;
+
+ } else if (el=="mod") {
+
+ Ref<EventStreamChibi> esc( memnew( EventStreamChibi ) );
+ CPLoader_MOD loader(&f);
+ CPLoader::Error err=loader.load_song(p_path.utf8().get_data(),&esc->song,false);
+ ERR_FAIL_COND_V(err!=CPLoader::FILE_OK,RES());
+ if (r_error)
+ *r_error=OK;
+ return esc;
+ }
+
+ return RES();
+
+}
+
+void ResourceFormatLoaderChibi::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("it");
+ p_extensions->push_back("xm");
+ p_extensions->push_back("s3m");
+ p_extensions->push_back("mod");
+}
+bool ResourceFormatLoaderChibi::handles_type(const String& p_type) const {
+
+ return (p_type=="EventStreamChibi" || p_type=="EventStream");
+}
+
+String ResourceFormatLoaderChibi::get_resource_type(const String &p_path) const {
+ String el = p_path.extension().to_lower();
+ if (el=="it" || el=="s3m" || el=="xm" || el=="mod")
+ return "EventStreamChibi";
+ return "";
+}
+
+/////////////////////////////////////////////////////////////////
+void initialize_chibi() {
+
+ sample_manager = memnew( CPSampleManagerImpl );
+ resource_loader = memnew( ResourceFormatLoaderChibi );
+ ObjectTypeDB::register_type<EventStreamChibi>();
+ ResourceLoader::add_resource_format_loader( resource_loader );
+}
+
+void finalize_chibi() {
+
+ memdelete( sample_manager );
+ memdelete( resource_loader );
+}
+
diff --git a/modules/chibi/event_stream_chibi.h b/modules/chibi/event_stream_chibi.h
new file mode 100644
index 0000000000..cc7b0ace86
--- /dev/null
+++ b/modules/chibi/event_stream_chibi.h
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* event_stream_chibi.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef EVENT_STREAM_CHIBI_H
+#define EVENT_STREAM_CHIBI_H
+
+#include "scene/resources/event_stream.h"
+#include "cp_sample_manager.h"
+#include "cp_mixer.h"
+#include "cp_song.h"
+#include "cp_file_access_wrapper.h"
+#include "cp_player_data.h"
+#include "resource.h"
+#include "servers/audio_server.h"
+#include "os/file_access.h"
+#include "io/resource_loader.h"
+
+/** SAMPLE MANAGER **/
+
+class CPSampleManagerImpl : public CPSampleManager {
+
+ struct SampleData {
+
+ RID rid;
+ bool stereo;
+ bool is16;
+ int len;
+ int mixfreq;
+ int loop_begin;
+ int loop_end;
+ int locks;
+ DVector<uint8_t> lock;
+ DVector<uint8_t>::Write w;
+ CPSample_Loop_Type loop_type;
+ };
+
+
+ _FORCE_INLINE_ SampleData* _getsd(CPSample_ID p_id) {
+
+ return ((SampleData*)p_id._private);
+ }
+ Set<SampleData*> valid;
+
+public:
+
+ _FORCE_INLINE_ RID get_rid(CPSample_ID p_id) { return _getsd(p_id)->rid; }
+ virtual CPSample_ID create(bool p_16bits,bool p_stereo,int32_t p_len);
+ virtual void recreate(CPSample_ID p_id,bool p_16bits,bool p_stereo,int32_t p_len);
+ virtual void destroy(CPSample_ID p_id);
+ virtual bool check(CPSample_ID p_id); // return false if invalid
+
+ virtual void set_c5_freq(CPSample_ID p_id,int32_t p_freq);
+ virtual void set_loop_begin(CPSample_ID p_id,int32_t p_begin);
+ virtual void set_loop_end(CPSample_ID p_id,int32_t p_end);
+ virtual void set_loop_type(CPSample_ID p_id,CPSample_Loop_Type p_type);
+ virtual void set_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len);
+
+
+ virtual int32_t get_loop_begin(CPSample_ID p_id);
+ virtual int32_t get_loop_end(CPSample_ID p_id);
+ virtual CPSample_Loop_Type get_loop_type(CPSample_ID p_id);
+ virtual int32_t get_c5_freq(CPSample_ID p_id);
+ virtual int32_t get_size(CPSample_ID p_id);
+ virtual bool is_16bits(CPSample_ID p_id);
+ virtual bool is_stereo(CPSample_ID p_id);
+ virtual bool lock_data(CPSample_ID p_id);
+ virtual void *get_data(CPSample_ID p_id); /* WARNING: Not all sample managers
+may be able to implement this, it depends on the mixer in use! */
+ virtual int16_t get_data(CPSample_ID p_id, int p_sample, int p_channel=0); /// Does not need locking
+ virtual void set_data(CPSample_ID p_id, int p_sample, int16_t p_data,int p_channel=0); /// Does not need locking
+ virtual void unlock_data(CPSample_ID p_id);
+
+ virtual void get_chunk(CPSample_ID p_id,int32_t p_index,void *p_data,int p_data_len);
+
+};
+
+
+/** MIXER **/
+
+class CPMixerImpl : public CPMixer {
+
+ enum {
+ MAX_VOICES=64
+ };
+
+ struct Voice {
+
+ AudioMixer::ChannelID channel;
+ CPSample_ID sample;
+ float freq_mult;
+ float reverb;
+ Voice() { reverb=0.0; }
+ };
+
+ Voice voices[MAX_VOICES];
+
+
+ int callback_interval;
+ int callback_timeout;
+ void (*callback)(void*);
+ void *userdata;
+ float voice_scale;
+ float tempo_scale;
+ float pitch_scale;
+ AudioMixer::ReverbRoomType reverb_type;
+ AudioMixer *mixer;
+public:
+
+ void process_usecs(int p_usec,float p_volume,float p_pitch_scale,float p_tempo_scale);
+
+ /* Callback */
+
+ virtual void set_callback_interval(int p_interval_us); //in usecs, for tracker it's 2500000/tempo
+ virtual void set_callback(void (*p_callback)(void*),void *p_userdata);
+
+ /* Voice Control */
+
+ virtual void setup_voice(int p_voice_index,CPSample_ID p_sample_id,int32_t p_start_index) ;
+ virtual void stop_voice(int p_voice_index) ;
+ virtual void set_voice_frequency(int p_voice_index,int32_t p_freq) ; //in freq*FREQUENCY_BITS
+ virtual void set_voice_panning(int p_voice_index,int p_pan) ;
+ virtual void set_voice_volume(int p_voice_index,int p_vol) ;
+ virtual void set_voice_filter(int p_filter,bool p_enabled,uint8_t p_cutoff, uint8_t p_resonance );
+ virtual void set_voice_reverb_send(int p_voice_index,int p_reverb);
+ virtual void set_voice_chorus_send(int p_voice_index,int p_chorus); /* 0 - 255 */
+
+ virtual void set_reverb_mode(ReverbMode p_mode);
+ virtual void set_chorus_params(unsigned int p_delay_ms,unsigned int p_separation_ms,unsigned int p_depth_ms10,unsigned int p_speed_hz10);
+
+
+ /* Info retrieving */
+
+ virtual int32_t get_voice_sample_pos_index(int p_voice_index) ;
+ virtual int get_voice_panning(int p_voice_index) ;
+ virtual int get_voice_volume(int p_voice_index) ;
+ virtual CPSample_ID get_voice_sample_id(int p_voice_index) ;
+ virtual bool is_voice_active(int p_voice_index);
+ virtual int get_active_voice_count() { return 0; }
+ virtual int get_total_voice_count() { return MAX_VOICES; }
+
+
+ virtual uint32_t get_mix_frequency() { return 0; }
+
+ /* Methods below only work with software mixers, meant for software-based sound drivers, hardware mixers ignore them */
+ virtual int32_t process(int32_t p_frames) { return 0; }
+ virtual int32_t *get_mixdown_buffer_ptr() { return NULL; }
+ virtual void set_mix_frequency(int32_t p_mix_frequency) {};
+
+ CPMixerImpl(AudioMixer *p_mixer=NULL);
+ virtual ~CPMixerImpl() {}
+};
+
+/** FILE ACCESS **/
+
+class CPFileAccessWrapperImpl : public CPFileAccessWrapper {
+
+ FileAccess *f;
+public:
+
+
+ virtual Error open(const char *p_filename, int p_mode_flags);
+ virtual void close();
+
+ virtual void seek(uint32_t p_position);
+ virtual void seek_end();
+ virtual uint32_t get_pos();
+
+ virtual bool eof_reached();
+
+ virtual uint8_t get_byte();
+ virtual void get_byte_array(uint8_t *p_dest,int p_elements);
+ virtual void get_word_array(uint16_t *p_dest,int p_elements);
+
+ virtual uint16_t get_word();
+ virtual uint32_t get_dword();
+
+ virtual void set_endian_conversion(bool p_swap);
+ virtual bool is_open();
+
+ virtual Error get_error();
+
+ virtual void store_byte(uint8_t p_dest);
+ virtual void store_byte_array(const uint8_t *p_dest,int p_elements);
+
+ virtual void store_word(uint16_t p_dest);
+ virtual void store_dword(uint32_t p_dest);
+
+ CPFileAccessWrapperImpl() { f=NULL; }
+ virtual ~CPFileAccessWrapperImpl(){ if (f) memdelete(f); }
+
+};
+
+
+
+/////////////////////
+
+class EventStreamChibi;
+
+class EventStreamPlaybackChibi : public EventStreamPlayback {
+
+ OBJ_TYPE(EventStreamPlaybackChibi,EventStreamPlayback);
+
+ CPMixerImpl mixer;
+ uint64_t total_usec;
+ Ref<EventStreamChibi> stream;
+ mutable CPPlayer *player;
+ bool loop;
+ int last_order;
+ int loops;
+ virtual Error _play();
+ virtual bool _update(AudioMixer* p_mixer, uint64_t p_usec);
+ virtual void _stop();
+ float volume;
+ float tempo_scale;
+ float pitch_scale;
+
+
+public:
+
+
+ virtual void set_paused(bool p_paused);
+ virtual bool is_paused() const;
+
+ virtual void set_loop(bool p_loop);
+ virtual bool is_loop_enabled() const;
+
+ virtual int get_loop_count() const;
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+ virtual void set_volume(float p_vol);
+ virtual float get_volume() const;
+
+ virtual void set_pitch_scale(float p_pitch_scale);
+ virtual float get_pitch_scale() const;
+
+ virtual void set_tempo_scale(float p_tempo_scale);
+ virtual float get_tempo_scale() const;
+
+ virtual void set_channel_volume(int p_channel,float p_volume);
+ virtual float get_channel_volume(int p_channel) const;
+
+ virtual float get_last_note_time(int p_channel) const;
+
+ EventStreamPlaybackChibi(Ref<EventStreamChibi> p_stream=Ref<EventStreamChibi>());
+ ~EventStreamPlaybackChibi();
+};
+
+
+class EventStreamChibi : public EventStream {
+
+ OBJ_TYPE(EventStreamChibi,EventStream);
+
+friend class ResourceFormatLoaderChibi;
+friend class EventStreamPlaybackChibi;
+ //I think i didn't know what const was when i wrote this more than a decade ago
+ //so it goes mutable :(
+ mutable CPSong song;
+
+
+public:
+
+ virtual Ref<EventStreamPlayback> instance_playback();
+
+ virtual String get_stream_name() const;
+
+ virtual float get_length() const;
+
+ virtual int get_channel_count() const { return 64; } //tracker limit
+
+ EventStreamChibi();
+};
+
+
+class ResourceFormatLoaderChibi : public ResourceFormatLoader {
+
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+void initialize_chibi();
+void finalize_chibi();
+
+#endif // EVENT_STREAM_CHIBI_H
diff --git a/modules/chibi/register_types.cpp b/modules/chibi/register_types.cpp
new file mode 100644
index 0000000000..b2ba16fa03
--- /dev/null
+++ b/modules/chibi/register_types.cpp
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "event_stream_chibi.h"
+
+void register_chibi_types() {
+
+ initialize_chibi();
+}
+
+void unregister_chibi_types() {
+
+ finalize_chibi();
+}
diff --git a/modules/chibi/register_types.h b/modules/chibi/register_types.h
new file mode 100644
index 0000000000..159823b85d
--- /dev/null
+++ b/modules/chibi/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_chibi_types();
+void unregister_chibi_types();
diff --git a/modules/cscript/SCsub b/modules/cscript/SCsub
index 403fe68f66..0882406761 100644
--- a/modules/cscript/SCsub
+++ b/modules/cscript/SCsub
@@ -1,5 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/dds/SCsub b/modules/dds/SCsub
new file mode 100644
index 0000000000..3d92ff02d6
--- /dev/null
+++ b/modules/dds/SCsub
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_dds = env_modules.Clone()
+
+env_dds.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/dds/config.py b/modules/dds/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/dds/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
new file mode 100644
index 0000000000..0d28e2bbef
--- /dev/null
+++ b/modules/dds/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "texture_loader_dds.h"
+
+static ResourceFormatDDS *resource_loader_dds = NULL;
+
+void register_dds_types() {
+
+ resource_loader_dds = memnew( ResourceFormatDDS );
+ ResourceLoader::add_resource_format_loader(resource_loader_dds);
+}
+
+void unregister_dds_types() {
+
+ memdelete(resource_loader_dds);
+}
diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h
new file mode 100644
index 0000000000..f9ecfb8ef9
--- /dev/null
+++ b/modules/dds/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_dds_types();
+void unregister_dds_types();
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
new file mode 100644
index 0000000000..0cc84f02f7
--- /dev/null
+++ b/modules/dds/texture_loader_dds.cpp
@@ -0,0 +1,485 @@
+/*************************************************************************/
+/* texture_loader_dds.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "texture_loader_dds.h"
+#include "os/file_access.h"
+
+
+enum {
+ DDS_MAGIC=0x20534444,
+ DDSD_CAPS=0x00000001,
+ DDSD_PIXELFORMAT=0x00001000,
+ DDSD_PITCH=0x00000008,
+ DDSD_LINEARSIZE=0x00080000,
+ DDSD_MIPMAPCOUNT=0x00020000,
+ DDPF_FOURCC=0x00000004,
+ DDPF_ALPHAPIXELS=0x00000001,
+ DDPF_INDEXED=0x00000020,
+ DDPF_RGB=0x00000040,
+};
+
+enum DDSFormat {
+
+ DDS_DXT1,
+ DDS_DXT3,
+ DDS_DXT5,
+ DDS_ATI1,
+ DDS_ATI2,
+ DDS_BGRA8,
+ DDS_BGR8,
+ DDS_RGBA8, //flipped in dds
+ DDS_RGB8, //flipped in dds
+ DDS_BGR5A1,
+ DDS_BGR565,
+ DDS_BGR10A2,
+ DDS_INDEXED,
+ DDS_LUMINANCE,
+ DDS_LUMINANCE_ALPHA,
+ DDS_MAX
+};
+
+struct DDSFormatInfo {
+ const char *name;
+ bool compressed;
+ bool palette;
+ uint32_t divisor;
+ uint32_t block_size;
+ Image::Format format;
+};
+
+
+static const DDSFormatInfo dds_format_info[DDS_MAX]={
+ {"DXT1",true,false,4,8,Image::FORMAT_BC1},
+ {"DXT3",true,false,4,16,Image::FORMAT_BC2},
+ {"DXT5",true,false,4,16,Image::FORMAT_BC3},
+ {"ATI1",true,false,4,8,Image::FORMAT_BC4},
+ {"ATI2",true,false,4,16,Image::FORMAT_BC5},
+ {"BGRA8",false,false,1,4,Image::FORMAT_RGBA},
+ {"BGR8",false,false,1,3,Image::FORMAT_RGB},
+ {"RGBA8",false,false,1,4,Image::FORMAT_RGBA},
+ {"RGB8",false,false,1,3,Image::FORMAT_RGB},
+ {"BGR5A1",false,false,1,2,Image::FORMAT_RGBA},
+ {"BGR565",false,false,1,2,Image::FORMAT_RGB},
+ {"BGR10A2",false,false,1,4,Image::FORMAT_RGBA},
+ {"INDEXED",false,true,1,1,Image::FORMAT_INDEXED},
+ {"GRAYSCALE",false,false,1,1,Image::FORMAT_GRAYSCALE},
+ {"GRAYSCALE_ALPHA",false,false,1,2,Image::FORMAT_GRAYSCALE_ALPHA}
+};
+
+
+RES ResourceFormatDDS::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_CANT_OPEN;
+
+ Error err;
+ FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err);
+ if (!f)
+ return RES();
+
+ FileAccessRef fref(f);
+ if (r_error)
+ *r_error=ERR_FILE_CORRUPT;
+
+ ERR_EXPLAIN("Unable to open DDS texture file: "+p_path);
+ ERR_FAIL_COND_V(err!=OK,RES());
+
+ uint32_t magic = f->get_32();
+ uint32_t hsize = f->get_32();
+ uint32_t flags = f->get_32();
+ uint32_t width = f->get_32();
+ uint32_t height = f->get_32();
+ uint32_t pitch = f->get_32();
+ uint32_t depth = f->get_32();
+ uint32_t mipmaps = f->get_32();
+
+ //skip 11
+ for(int i=0;i<11;i++)
+ f->get_32();
+
+ //validate
+
+ if (magic!=DDS_MAGIC || hsize!=124 || !(flags&DDSD_PIXELFORMAT) || !(flags&DDSD_CAPS)) {
+
+ ERR_EXPLAIN("Invalid or Unsupported DDS texture file: "+p_path);
+ ERR_FAIL_V(RES());
+ }
+
+
+ uint32_t format_size = f->get_32();
+ uint32_t format_flags = f->get_32();
+ uint32_t format_fourcc = f->get_32();
+ uint32_t format_rgb_bits = f->get_32();
+ uint32_t format_red_mask = f->get_32();
+ uint32_t format_green_mask = f->get_32();
+ uint32_t format_blue_mask = f->get_32();
+ uint32_t format_alpha_mask = f->get_32();
+
+ uint32_t caps_1 = f->get_32();
+ uint32_t caps_2 = f->get_32();
+ uint32_t caps_ddsx = f->get_32();
+
+ //reserved skip
+ f->get_32();
+ f->get_32();
+
+ /*print_line("DDS width: "+itos(width));
+ print_line("DDS height: "+itos(height));
+ print_line("DDS mipmaps: "+itos(mipmaps));*/
+
+ //printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
+ //printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+
+ //must avoid this later
+ while(f->get_pos()<128)
+ f->get_8();
+
+
+ DDSFormat dds_format;
+
+ if (format_flags&DDPF_FOURCC && format_fourcc==0x31545844) { //'1TXD'
+
+ dds_format=DDS_DXT1;
+ } else if (format_flags&DDPF_FOURCC && format_fourcc==0x33545844) { //'3TXD'
+
+ dds_format=DDS_DXT3;
+
+ } else if (format_flags&DDPF_FOURCC && format_fourcc==0x35545844) { //'5TXD'
+
+ dds_format=DDS_DXT5;
+ } else if (format_flags&DDPF_FOURCC && format_fourcc==0x31495441) { //'1ITA'
+
+ dds_format=DDS_ATI1;
+ } else if (format_flags&DDPF_FOURCC && format_fourcc==0x32495441) { //'2ITA'
+
+ dds_format=DDS_ATI2;
+
+ } else if (format_flags&DDPF_RGB && format_flags&DDPF_ALPHAPIXELS && format_rgb_bits==32 && format_red_mask==0xff0000 && format_green_mask==0xff00 && format_blue_mask==0xff && format_alpha_mask==0xff000000) {
+
+ dds_format=DDS_BGRA8;
+ } else if (format_flags&DDPF_RGB && !(format_flags&DDPF_ALPHAPIXELS ) && format_rgb_bits==24 && format_red_mask==0xff0000 && format_green_mask==0xff00 && format_blue_mask==0xff) {
+
+ dds_format=DDS_BGR8;
+ } else if (format_flags&DDPF_RGB && format_flags&DDPF_ALPHAPIXELS && format_rgb_bits==32 && format_red_mask==0xff && format_green_mask==0xff00 && format_blue_mask==0xff0000 && format_alpha_mask==0xff000000) {
+
+ dds_format=DDS_RGBA8;
+ } else if (format_flags&DDPF_RGB && !(format_flags&DDPF_ALPHAPIXELS ) && format_rgb_bits==24 && format_red_mask==0xff && format_green_mask==0xff00 && format_blue_mask==0xff0000) {
+
+ dds_format=DDS_RGB8;
+
+ } else if (format_flags&DDPF_RGB && format_flags&DDPF_ALPHAPIXELS && format_rgb_bits==16 && format_red_mask==0x00007c00 && format_green_mask==0x000003e0 && format_blue_mask==0x0000001f && format_alpha_mask==0x00008000) {
+
+ dds_format=DDS_BGR5A1;
+ } else if (format_flags&DDPF_RGB && format_flags&DDPF_ALPHAPIXELS && format_rgb_bits==32 && format_red_mask==0x3ff00000 && format_green_mask==0xffc00 && format_blue_mask==0x3ff && format_alpha_mask==0xc0000000) {
+
+ dds_format=DDS_BGR10A2;
+ } else if (format_flags&DDPF_RGB && !(format_flags&DDPF_ALPHAPIXELS) && format_rgb_bits==16 && format_red_mask==0x0000f800 && format_green_mask==0x000007e0 && format_blue_mask==0x0000001f) {
+
+ dds_format=DDS_BGR565;
+ } else if (!(format_flags&DDPF_ALPHAPIXELS) && format_rgb_bits==8 && format_red_mask==0xff && format_green_mask==0xff && format_blue_mask==0xff) {
+
+ dds_format=DDS_LUMINANCE;
+ } else if ((format_flags&DDPF_ALPHAPIXELS) && format_rgb_bits==16 && format_red_mask==0xff && format_green_mask==0xff && format_blue_mask==0xff && format_alpha_mask==0xff00) {
+
+ dds_format=DDS_LUMINANCE_ALPHA;
+ } else if (format_flags&DDPF_INDEXED && format_rgb_bits==8) {
+
+ dds_format=DDS_BGR565;
+ } else {
+
+ printf("unrecognized fourcc %x format_flags: %x - rgbbits %i - red_mask %x green mask %x blue mask %x alpha mask %x\n",format_fourcc,format_flags,format_rgb_bits,format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+ ERR_EXPLAIN("Unrecognized or Unsupported color layout in DDS: "+p_path);
+
+ ERR_FAIL_V(RES());
+
+ }
+
+ if (!(flags&DDSD_MIPMAPCOUNT))
+ mipmaps=1;
+
+// print_line("found format: "+String(dds_format_info[dds_format].name));
+
+ DVector<uint8_t> src_data;
+
+ const DDSFormatInfo &info=dds_format_info[dds_format];
+ uint32_t w = width;
+ uint32_t h = height;
+
+
+ if (info.compressed) {
+ //compressed bc
+
+ uint32_t size = MAX( info.divisor, w )/info.divisor * MAX( info.divisor, h )/info.divisor * info.block_size;
+ ERR_FAIL_COND_V( size!=pitch, RES() );
+ ERR_FAIL_COND_V( !(flags&DDSD_LINEARSIZE), RES() );
+
+ for(uint32_t i=1;i<mipmaps;i++) {
+
+ w=MAX(1,w>>1);
+ h=MAX(1,h>>1);
+ uint32_t bsize = MAX( info.divisor, w )/info.divisor * MAX( info.divisor, h )/info.divisor * info.block_size;
+ //printf("%i x %i - block: %i\n",w,h,bsize);
+ size+= bsize;
+ }
+
+ src_data.resize(size);
+ DVector<uint8_t>::Write wb = src_data.write();
+ f->get_buffer(wb.ptr(),size);
+ wb=DVector<uint8_t>::Write();
+
+ } else if (info.palette) {
+
+ //indexed
+ ERR_FAIL_COND_V( !(flags&DDSD_PITCH), RES());
+ ERR_FAIL_COND_V( format_rgb_bits!=8, RES() );
+
+ uint32_t size = pitch*height;
+ ERR_FAIL_COND_V( size != width*height * info.block_size, RES());
+
+ uint8_t pallete[256*4];
+ f->get_buffer(pallete,256*4);
+
+ int colsize=3;
+ for(int i=0;i<256;i++) {
+
+ if (pallete[i*4+3]<255)
+ colsize=4;
+ }
+
+ int w = width;
+ int h = height;
+
+ for(uint32_t i=1;i<mipmaps;i++) {
+
+ w=(w+1)>>1;
+ h=(h+1)>>1;
+ size+= w*h*info.block_size;
+ }
+
+ src_data.resize(size + 256*colsize );
+ DVector<uint8_t>::Write wb = src_data.write();
+ f->get_buffer(wb.ptr(),size);
+
+ for(int i=0;i<256;i++) {
+
+ int dst_ofs = size+i*colsize;
+ int src_ofs = i*4;
+ wb[dst_ofs+0]=pallete[src_ofs+2];
+ wb[dst_ofs+1]=pallete[src_ofs+1];
+ wb[dst_ofs+2]=pallete[src_ofs+0];
+ if (colsize==4)
+ wb[dst_ofs+3]=pallete[src_ofs+3];
+ }
+
+
+ wb=DVector<uint8_t>::Write();
+ } else {
+ //uncompressed generic...
+
+ uint32_t size = width*height*info.block_size;
+
+
+ for(uint32_t i=1;i<mipmaps;i++) {
+
+ w=(w+1)>>1;
+ h=(h+1)>>1;
+ size+= w*h*info.block_size;
+ }
+
+ if (dds_format==DDS_BGR565)
+ size=size*3/2;
+ else if (dds_format==DDS_BGR5A1)
+ size=size*2;
+
+ src_data.resize(size);
+ DVector<uint8_t>::Write wb = src_data.write();
+ f->get_buffer(wb.ptr(),size);
+
+
+ switch(dds_format) {
+
+ case DDS_BGR5A1: {
+
+ // TO RGBA
+ int colcount = size/4;
+
+ for(int i=colcount-1;i>=0;i--) {
+
+ int src_ofs = i*2;
+ int dst_ofs = i*4;
+
+ uint8_t a=wb[src_ofs+1]&0x80;
+ uint8_t b= wb[src_ofs]&0x1F;
+ uint8_t g= (wb[src_ofs]>>5) | ((wb[src_ofs+1]&0x3)<<3);
+ uint8_t r= (wb[src_ofs+1]>>2)&0x1F;
+ wb[dst_ofs+0]=r<<3;
+ wb[dst_ofs+1]=g<<3;
+ wb[dst_ofs+2]=b<<3;
+ wb[dst_ofs+3]=a?255:0;
+ }
+ } break;
+ case DDS_BGR565: {
+
+ int colcount = size/3;
+
+ for(int i=colcount-1;i>=0;i--) {
+
+ int src_ofs = i*2;
+ int dst_ofs = i*3;
+
+ uint8_t b= wb[src_ofs]&0x1F;
+ uint8_t g= (wb[src_ofs]>>5) | ((wb[src_ofs+1]&0x7)<<3);
+ uint8_t r= wb[src_ofs+1]>>3;
+ wb[dst_ofs+0]=r<<3;
+ wb[dst_ofs+1]=g<<2;
+ wb[dst_ofs+2]=b<<3;//b<<3;
+
+ }
+
+ } break;
+ case DDS_BGR10A2: {
+
+ // TO RGBA
+ int colcount = size/4;
+
+ for(int i=colcount-1;i>=0;i--) {
+
+ int ofs = i*4;
+
+ uint32_t w32 = uint32_t(wb[ofs+0]) | (uint32_t(wb[ofs+1])<<8) | (uint32_t(wb[ofs+2])<<16) | (uint32_t(wb[ofs+3])<<24);
+
+ uint8_t a= (w32&0xc0000000) >> 24;
+ uint8_t r= (w32&0x3ff00000) >> 22;
+ uint8_t g= (w32&0xffc00) >> 12;
+ uint8_t b= (w32&0x3ff) >> 2;
+
+
+ wb[ofs+0]=r;
+ wb[ofs+1]=g;
+ wb[ofs+2]=b;
+ wb[ofs+3]=a==0xc0 ? 255 : a; //0xc0 should be opaque
+
+ }
+ } break;
+ case DDS_BGRA8: {
+
+ int colcount = size/4;
+
+ for(int i=0;i<colcount;i++) {
+
+ SWAP( wb[i*4+0],wb[i*4+2] );
+ }
+
+ } break;
+ case DDS_BGR8: {
+
+ int colcount = size/3;
+
+ for(int i=0;i<colcount;i++) {
+
+ SWAP( wb[i*3+0],wb[i*3+2] );
+ }
+ } break;
+ case DDS_RGBA8: {
+
+ /* do nothing either
+ int colcount = size/4;
+
+ for(int i=0;i<colcount;i++) {
+
+ uint8_t r = wb[i*4+1];
+ uint8_t g = wb[i*4+2];
+ uint8_t b = wb[i*4+3];
+ uint8_t a = wb[i*4+0];
+
+ wb[i*4+0]=r;
+ wb[i*4+1]=g;
+ wb[i*4+2]=b;
+ wb[i*4+3]=a;
+ }
+ */
+ } break;
+ case DDS_RGB8: {
+
+ // do nothing
+ /*
+ int colcount = size/3;
+
+ for(int i=0;i<colcount;i++) {
+
+ SWAP( wb[i*3+0],wb[i*3+2] );
+ }*/
+ } break;
+ case DDS_LUMINANCE: {
+
+ // do nothing i guess?
+
+ } break;
+ case DDS_LUMINANCE_ALPHA: {
+
+ // do nothing i guess?
+
+ } break;
+
+ default: {}
+
+ }
+
+ wb=DVector<uint8_t>::Write();
+ }
+
+
+ Image img(width,height,mipmaps-1,info.format,src_data);
+
+ Ref<ImageTexture> texture = memnew( ImageTexture );
+ texture->create_from_image(img);
+
+ if (r_error)
+ *r_error=OK;
+
+
+ return texture;
+
+}
+
+void ResourceFormatDDS::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("dds");
+}
+
+bool ResourceFormatDDS::handles_type(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Texture");
+}
+
+String ResourceFormatDDS::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="dds")
+ return "ImageTexture";
+ return "";
+}
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
new file mode 100644
index 0000000000..371eb1858c
--- /dev/null
+++ b/modules/dds/texture_loader_dds.h
@@ -0,0 +1,46 @@
+/*************************************************************************/
+/* texture_loader_dds.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef TEXTURE_LOADER_DDS_H
+#define TEXTURE_LOADER_DDS_H
+
+#include "scene/resources/texture.h"
+#include "io/resource_loader.h"
+
+class ResourceFormatDDS : public ResourceFormatLoader{
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+ virtual ~ResourceFormatDDS() {}
+};
+
+#endif // TEXTURE_LOADER_DDS_H
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index d2bc8801e4..5175803f44 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -1,8 +1,28 @@
+#!/usr/bin/env python
+
Import('env')
+Import('env_modules')
+
+# Thirdparty source files
+
+env_enet = env_modules.Clone()
+
+if (env["enet"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/enet/"
+ thirdparty_sources = [
+ "callbacks.c",
+ "compress.c",
+ "host.c",
+ "list.c",
+ "packet.c",
+ "peer.c",
+ "protocol.c",
+ "unix.c",
+ "win32.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env.add_source_files(env.modules_sources,"*.cpp")
-env.add_source_files(env.modules_sources,"*.c")
-#TODO: Make it possible to build against system enet
-env.Append(CPPPATH = ["#modules/enet"])
+ env_enet.add_source_files(env.modules_sources, thirdparty_sources)
+ env_enet.Append(CPPPATH = [thirdparty_dir])
-Export('env')
+env_enet.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/enet/callbacks.c b/modules/enet/callbacks.c
deleted file mode 100644
index b3990af1fb..0000000000
--- a/modules/enet/callbacks.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- @file callbacks.c
- @brief ENet callback functions
-*/
-#define ENET_BUILDING_LIB 1
-#include "enet/enet.h"
-
-static ENetCallbacks callbacks = { malloc, free, abort };
-
-int
-enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits)
-{
- if (version < ENET_VERSION_CREATE (1, 3, 0))
- return -1;
-
- if (inits -> malloc != NULL || inits -> free != NULL)
- {
- if (inits -> malloc == NULL || inits -> free == NULL)
- return -1;
-
- callbacks.malloc = inits -> malloc;
- callbacks.free = inits -> free;
- }
-
- if (inits -> no_memory != NULL)
- callbacks.no_memory = inits -> no_memory;
-
- return enet_initialize ();
-}
-
-ENetVersion
-enet_linked_version (void)
-{
- return ENET_VERSION;
-}
-
-void *
-enet_malloc (size_t size)
-{
- void * memory = callbacks.malloc (size);
-
- if (memory == NULL)
- callbacks.no_memory ();
-
- return memory;
-}
-
-void
-enet_free (void * memory)
-{
- callbacks.free (memory);
-}
-
diff --git a/modules/enet/compress.c b/modules/enet/compress.c
deleted file mode 100644
index 784489a787..0000000000
--- a/modules/enet/compress.c
+++ /dev/null
@@ -1,654 +0,0 @@
-/**
- @file compress.c
- @brief An adaptive order-2 PPM range coder
-*/
-#define ENET_BUILDING_LIB 1
-#include <string.h>
-#include "enet/enet.h"
-
-typedef struct _ENetSymbol
-{
- /* binary indexed tree of symbols */
- enet_uint8 value;
- enet_uint8 count;
- enet_uint16 under;
- enet_uint16 left, right;
-
- /* context defined by this symbol */
- enet_uint16 symbols;
- enet_uint16 escapes;
- enet_uint16 total;
- enet_uint16 parent;
-} ENetSymbol;
-
-/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */
-enum
-{
- ENET_RANGE_CODER_TOP = 1<<24,
- ENET_RANGE_CODER_BOTTOM = 1<<16,
-
- ENET_CONTEXT_SYMBOL_DELTA = 3,
- ENET_CONTEXT_SYMBOL_MINIMUM = 1,
- ENET_CONTEXT_ESCAPE_MINIMUM = 1,
-
- ENET_SUBCONTEXT_ORDER = 2,
- ENET_SUBCONTEXT_SYMBOL_DELTA = 2,
- ENET_SUBCONTEXT_ESCAPE_DELTA = 5
-};
-
-/* context exclusion roughly halves compression speed, so disable for now */
-#undef ENET_CONTEXT_EXCLUSION
-
-typedef struct _ENetRangeCoder
-{
- /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */
- ENetSymbol symbols[4096];
-} ENetRangeCoder;
-
-void *
-enet_range_coder_create (void)
-{
- ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder));
- if (rangeCoder == NULL)
- return NULL;
-
- return rangeCoder;
-}
-
-void
-enet_range_coder_destroy (void * context)
-{
- ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
- if (rangeCoder == NULL)
- return;
-
- enet_free (rangeCoder);
-}
-
-#define ENET_SYMBOL_CREATE(symbol, value_, count_) \
-{ \
- symbol = & rangeCoder -> symbols [nextSymbol ++]; \
- symbol -> value = value_; \
- symbol -> count = count_; \
- symbol -> under = count_; \
- symbol -> left = 0; \
- symbol -> right = 0; \
- symbol -> symbols = 0; \
- symbol -> escapes = 0; \
- symbol -> total = 0; \
- symbol -> parent = 0; \
-}
-
-#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \
-{ \
- ENET_SYMBOL_CREATE (context, 0, 0); \
- (context) -> escapes = escapes_; \
- (context) -> total = escapes_ + 256*minimum; \
- (context) -> symbols = 0; \
-}
-
-static enet_uint16
-enet_symbol_rescale (ENetSymbol * symbol)
-{
- enet_uint16 total = 0;
- for (;;)
- {
- symbol -> count -= symbol->count >> 1;
- symbol -> under = symbol -> count;
- if (symbol -> left)
- symbol -> under += enet_symbol_rescale (symbol + symbol -> left);
- total += symbol -> under;
- if (! symbol -> right) break;
- symbol += symbol -> right;
- }
- return total;
-}
-
-#define ENET_CONTEXT_RESCALE(context, minimum) \
-{ \
- (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \
- (context) -> escapes -= (context) -> escapes >> 1; \
- (context) -> total += (context) -> escapes + 256*minimum; \
-}
-
-#define ENET_RANGE_CODER_OUTPUT(value) \
-{ \
- if (outData >= outEnd) \
- return 0; \
- * outData ++ = value; \
-}
-
-#define ENET_RANGE_CODER_ENCODE(under, count, total) \
-{ \
- encodeRange /= (total); \
- encodeLow += (under) * encodeRange; \
- encodeRange *= (count); \
- for (;;) \
- { \
- if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \
- { \
- if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
- encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
- } \
- ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
- encodeRange <<= 8; \
- encodeLow <<= 8; \
- } \
-}
-
-#define ENET_RANGE_CODER_FLUSH \
-{ \
- while (encodeLow) \
- { \
- ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \
- encodeLow <<= 8; \
- } \
-}
-
-#define ENET_RANGE_CODER_FREE_SYMBOLS \
-{ \
- if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \
- { \
- nextSymbol = 0; \
- ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \
- predicted = 0; \
- order = 0; \
- } \
-}
-
-#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \
-{ \
- under_ = value*minimum; \
- count_ = minimum; \
- if (! (context) -> symbols) \
- { \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- (context) -> symbols = symbol_ - (context); \
- } \
- else \
- { \
- ENetSymbol * node = (context) + (context) -> symbols; \
- for (;;) \
- { \
- if (value_ < node -> value) \
- { \
- node -> under += update; \
- if (node -> left) { node += node -> left; continue; } \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- node -> left = symbol_ - node; \
- } \
- else \
- if (value_ > node -> value) \
- { \
- under_ += node -> under; \
- if (node -> right) { node += node -> right; continue; } \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- node -> right = symbol_ - node; \
- } \
- else \
- { \
- count_ += node -> count; \
- under_ += node -> under - node -> count; \
- node -> under += update; \
- node -> count += update; \
- symbol_ = node; \
- } \
- break; \
- } \
- } \
-}
-
-#ifdef ENET_CONTEXT_EXCLUSION
-static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-#define ENET_CONTEXT_WALK(context, body) \
-{ \
- const ENetSymbol * node = (context) + (context) -> symbols; \
- const ENetSymbol * stack [256]; \
- size_t stackSize = 0; \
- while (node -> left) \
- { \
- stack [stackSize ++] = node; \
- node += node -> left; \
- } \
- for (;;) \
- { \
- body; \
- if (node -> right) \
- { \
- node += node -> right; \
- while (node -> left) \
- { \
- stack [stackSize ++] = node; \
- node += node -> left; \
- } \
- } \
- else \
- if (stackSize <= 0) \
- break; \
- else \
- node = stack [-- stackSize]; \
- } \
-}
-
-#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \
-ENET_CONTEXT_WALK(context, { \
- if (node -> value != value_) \
- { \
- enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \
- if (node -> value < value_) \
- under -= parentCount; \
- total -= parentCount; \
- } \
-})
-#endif
-
-size_t
-enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit)
-{
- ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
- enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
- const enet_uint8 * inData, * inEnd;
- enet_uint32 encodeLow = 0, encodeRange = ~0;
- ENetSymbol * root;
- enet_uint16 predicted = 0;
- size_t order = 0, nextSymbol = 0;
-
- if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0)
- return 0;
-
- inData = (const enet_uint8 *) inBuffers -> data;
- inEnd = & inData [inBuffers -> dataLength];
- inBuffers ++;
- inBufferCount --;
-
- ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
-
- for (;;)
- {
- ENetSymbol * subcontext, * symbol;
-#ifdef ENET_CONTEXT_EXCLUSION
- const ENetSymbol * childContext = & emptyContext;
-#endif
- enet_uint8 value;
- enet_uint16 count, under, * parent = & predicted, total;
- if (inData >= inEnd)
- {
- if (inBufferCount <= 0)
- break;
- inData = (const enet_uint8 *) inBuffers -> data;
- inEnd = & inData [inBuffers -> dataLength];
- inBuffers ++;
- inBufferCount --;
- }
- value = * inData ++;
-
- for (subcontext = & rangeCoder -> symbols [predicted];
- subcontext != root;
-#ifdef ENET_CONTEXT_EXCLUSION
- childContext = subcontext,
-#endif
- subcontext = & rangeCoder -> symbols [subcontext -> parent])
- {
- ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
- * parent = symbol - rangeCoder -> symbols;
- parent = & symbol -> parent;
- total = subcontext -> total;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
- ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0);
-#endif
- if (count > 0)
- {
- ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total);
- }
- else
- {
- if (subcontext -> escapes > 0 && subcontext -> escapes < total)
- ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total);
- subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
- subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
- }
- subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
- if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
- ENET_CONTEXT_RESCALE (subcontext, 0);
- if (count > 0) goto nextInput;
- }
-
- ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM);
- * parent = symbol - rangeCoder -> symbols;
- parent = & symbol -> parent;
- total = root -> total;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA)
- ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM);
-#endif
- ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total);
- root -> total += ENET_CONTEXT_SYMBOL_DELTA;
- if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
- ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
-
- nextInput:
- if (order >= ENET_SUBCONTEXT_ORDER)
- predicted = rangeCoder -> symbols [predicted].parent;
- else
- order ++;
- ENET_RANGE_CODER_FREE_SYMBOLS;
- }
-
- ENET_RANGE_CODER_FLUSH;
-
- return (size_t) (outData - outStart);
-}
-
-#define ENET_RANGE_CODER_SEED \
-{ \
- if (inData < inEnd) decodeCode |= * inData ++ << 24; \
- if (inData < inEnd) decodeCode |= * inData ++ << 16; \
- if (inData < inEnd) decodeCode |= * inData ++ << 8; \
- if (inData < inEnd) decodeCode |= * inData ++; \
-}
-
-#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total)))
-
-#define ENET_RANGE_CODER_DECODE(under, count, total) \
-{ \
- decodeLow += (under) * decodeRange; \
- decodeRange *= (count); \
- for (;;) \
- { \
- if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \
- { \
- if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \
- decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \
- } \
- decodeCode <<= 8; \
- if (inData < inEnd) \
- decodeCode |= * inData ++; \
- decodeRange <<= 8; \
- decodeLow <<= 8; \
- } \
-}
-
-#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \
-{ \
- under_ = 0; \
- count_ = minimum; \
- if (! (context) -> symbols) \
- { \
- createRoot; \
- } \
- else \
- { \
- ENetSymbol * node = (context) + (context) -> symbols; \
- for (;;) \
- { \
- enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \
- visitNode; \
- if (code >= after) \
- { \
- under_ += node -> under; \
- if (node -> right) { node += node -> right; continue; } \
- createRight; \
- } \
- else \
- if (code < after - before) \
- { \
- node -> under += update; \
- if (node -> left) { node += node -> left; continue; } \
- createLeft; \
- } \
- else \
- { \
- value_ = node -> value; \
- count_ += node -> count; \
- under_ = after - before; \
- node -> under += update; \
- node -> count += update; \
- symbol_ = node; \
- } \
- break; \
- } \
- } \
-}
-
-#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
-ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0)
-
-#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \
-ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \
- { \
- value_ = code / minimum; \
- under_ = code - code%minimum; \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- (context) -> symbols = symbol_ - (context); \
- }, \
- exclude (node -> value, after, before), \
- { \
- value_ = node->value + 1 + (code - after)/minimum; \
- under_ = code - (code - after)%minimum; \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- node -> right = symbol_ - node; \
- }, \
- { \
- value_ = node->value - 1 - (after - before - code - 1)/minimum; \
- under_ = code - (after - before - code - 1)%minimum; \
- ENET_SYMBOL_CREATE (symbol_, value_, update); \
- node -> left = symbol_ - node; \
- }) \
-
-#ifdef ENET_CONTEXT_EXCLUSION
-typedef struct _ENetExclude
-{
- enet_uint8 value;
- enet_uint16 under;
-} ENetExclude;
-
-#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \
-{ \
- enet_uint16 under = 0; \
- nextExclude = excludes; \
- ENET_CONTEXT_WALK (context, { \
- under += rangeCoder -> symbols [node -> parent].count + minimum; \
- nextExclude -> value = node -> value; \
- nextExclude -> under = under; \
- nextExclude ++; \
- }); \
- total -= under; \
-}
-
-#define ENET_CONTEXT_EXCLUDED(value_, after, before) \
-{ \
- size_t low = 0, high = nextExclude - excludes; \
- for(;;) \
- { \
- size_t mid = (low + high) >> 1; \
- const ENetExclude * exclude = & excludes [mid]; \
- if (value_ < exclude -> value) \
- { \
- if (low + 1 < high) \
- { \
- high = mid; \
- continue; \
- } \
- if (exclude > excludes) \
- after -= exclude [-1].under; \
- } \
- else \
- { \
- if (value_ > exclude -> value) \
- { \
- if (low + 1 < high) \
- { \
- low = mid; \
- continue; \
- } \
- } \
- else \
- before = 0; \
- after -= exclude -> under; \
- } \
- break; \
- } \
-}
-#endif
-
-#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before)
-
-size_t
-enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit)
-{
- ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context;
- enet_uint8 * outStart = outData, * outEnd = & outData [outLimit];
- const enet_uint8 * inEnd = & inData [inLimit];
- enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0;
- ENetSymbol * root;
- enet_uint16 predicted = 0;
- size_t order = 0, nextSymbol = 0;
-#ifdef ENET_CONTEXT_EXCLUSION
- ENetExclude excludes [256];
- ENetExclude * nextExclude = excludes;
-#endif
-
- if (rangeCoder == NULL || inLimit <= 0)
- return 0;
-
- ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM);
-
- ENET_RANGE_CODER_SEED;
-
- for (;;)
- {
- ENetSymbol * subcontext, * symbol, * patch;
-#ifdef ENET_CONTEXT_EXCLUSION
- const ENetSymbol * childContext = & emptyContext;
-#endif
- enet_uint8 value = 0;
- enet_uint16 code, under, count, bottom, * parent = & predicted, total;
-
- for (subcontext = & rangeCoder -> symbols [predicted];
- subcontext != root;
-#ifdef ENET_CONTEXT_EXCLUSION
- childContext = subcontext,
-#endif
- subcontext = & rangeCoder -> symbols [subcontext -> parent])
- {
- if (subcontext -> escapes <= 0)
- continue;
- total = subcontext -> total;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > 0)
- ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0);
-#endif
- if (subcontext -> escapes >= total)
- continue;
- code = ENET_RANGE_CODER_READ (total);
- if (code < subcontext -> escapes)
- {
- ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total);
- continue;
- }
- code -= subcontext -> escapes;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > 0)
- {
- ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED);
- }
- else
-#endif
- {
- ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED);
- }
- bottom = symbol - rangeCoder -> symbols;
- ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total);
- subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
- if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
- ENET_CONTEXT_RESCALE (subcontext, 0);
- goto patchContexts;
- }
-
- total = root -> total;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > 0)
- ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM);
-#endif
- code = ENET_RANGE_CODER_READ (total);
- if (code < root -> escapes)
- {
- ENET_RANGE_CODER_DECODE (0, root -> escapes, total);
- break;
- }
- code -= root -> escapes;
-#ifdef ENET_CONTEXT_EXCLUSION
- if (childContext -> total > 0)
- {
- ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED);
- }
- else
-#endif
- {
- ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED);
- }
- bottom = symbol - rangeCoder -> symbols;
- ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total);
- root -> total += ENET_CONTEXT_SYMBOL_DELTA;
- if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
- ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM);
-
- patchContexts:
- for (patch = & rangeCoder -> symbols [predicted];
- patch != subcontext;
- patch = & rangeCoder -> symbols [patch -> parent])
- {
- ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0);
- * parent = symbol - rangeCoder -> symbols;
- parent = & symbol -> parent;
- if (count <= 0)
- {
- patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA;
- patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA;
- }
- patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA;
- if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100)
- ENET_CONTEXT_RESCALE (patch, 0);
- }
- * parent = bottom;
-
- ENET_RANGE_CODER_OUTPUT (value);
-
- if (order >= ENET_SUBCONTEXT_ORDER)
- predicted = rangeCoder -> symbols [predicted].parent;
- else
- order ++;
- ENET_RANGE_CODER_FREE_SYMBOLS;
- }
-
- return (size_t) (outData - outStart);
-}
-
-/** @defgroup host ENet host functions
- @{
-*/
-
-/** Sets the packet compressor the host should use to the default range coder.
- @param host host to enable the range coder for
- @returns 0 on success, < 0 on failure
-*/
-int
-enet_host_compress_with_range_coder (ENetHost * host)
-{
- ENetCompressor compressor;
- memset (& compressor, 0, sizeof (compressor));
- compressor.context = enet_range_coder_create();
- if (compressor.context == NULL)
- return -1;
- compressor.compress = enet_range_coder_compress;
- compressor.decompress = enet_range_coder_decompress;
- compressor.destroy = enet_range_coder_destroy;
- enet_host_compress (host, & compressor);
- return 0;
-}
-
-/** @} */
-
-
diff --git a/modules/enet/config.py b/modules/enet/config.py
index ea7e83378a..368e97e152 100644
--- a/modules/enet/config.py
+++ b/modules/enet/config.py
@@ -1,11 +1,6 @@
-
def can_build(platform):
- return True
-
+ return True
def configure(env):
pass
-
-
-
diff --git a/modules/enet/enet/callbacks.h b/modules/enet/enet/callbacks.h
deleted file mode 100644
index 340a4a9896..0000000000
--- a/modules/enet/enet/callbacks.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- @file callbacks.h
- @brief ENet callbacks
-*/
-#ifndef __ENET_CALLBACKS_H__
-#define __ENET_CALLBACKS_H__
-
-#include <stdlib.h>
-
-typedef struct _ENetCallbacks
-{
- void * (ENET_CALLBACK * malloc) (size_t size);
- void (ENET_CALLBACK * free) (void * memory);
- void (ENET_CALLBACK * no_memory) (void);
-} ENetCallbacks;
-
-/** @defgroup callbacks ENet internal callbacks
- @{
- @ingroup private
-*/
-extern void * enet_malloc (size_t);
-extern void enet_free (void *);
-
-/** @} */
-
-#endif /* __ENET_CALLBACKS_H__ */
-
diff --git a/modules/enet/enet/enet.h b/modules/enet/enet/enet.h
deleted file mode 100644
index 650b199ee5..0000000000
--- a/modules/enet/enet/enet.h
+++ /dev/null
@@ -1,596 +0,0 @@
-/**
- @file enet.h
- @brief ENet public header file
-*/
-#ifndef __ENET_ENET_H__
-#define __ENET_ENET_H__
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-#include <stdlib.h>
-
-#ifdef _WIN32
-#include "enet/win32.h"
-#else
-#include "enet/unix.h"
-#endif
-
-#include "enet/types.h"
-#include "enet/protocol.h"
-#include "enet/list.h"
-#include "enet/callbacks.h"
-
-#define ENET_VERSION_MAJOR 1
-#define ENET_VERSION_MINOR 3
-#define ENET_VERSION_PATCH 13
-#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch))
-#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF)
-#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF)
-#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF)
-#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH)
-
-typedef enet_uint32 ENetVersion;
-
-struct _ENetHost;
-struct _ENetEvent;
-struct _ENetPacket;
-
-typedef enum _ENetSocketType
-{
- ENET_SOCKET_TYPE_STREAM = 1,
- ENET_SOCKET_TYPE_DATAGRAM = 2
-} ENetSocketType;
-
-typedef enum _ENetSocketWait
-{
- ENET_SOCKET_WAIT_NONE = 0,
- ENET_SOCKET_WAIT_SEND = (1 << 0),
- ENET_SOCKET_WAIT_RECEIVE = (1 << 1),
- ENET_SOCKET_WAIT_INTERRUPT = (1 << 2)
-} ENetSocketWait;
-
-typedef enum _ENetSocketOption
-{
- ENET_SOCKOPT_NONBLOCK = 1,
- ENET_SOCKOPT_BROADCAST = 2,
- ENET_SOCKOPT_RCVBUF = 3,
- ENET_SOCKOPT_SNDBUF = 4,
- ENET_SOCKOPT_REUSEADDR = 5,
- ENET_SOCKOPT_RCVTIMEO = 6,
- ENET_SOCKOPT_SNDTIMEO = 7,
- ENET_SOCKOPT_ERROR = 8,
- ENET_SOCKOPT_NODELAY = 9
-} ENetSocketOption;
-
-typedef enum _ENetSocketShutdown
-{
- ENET_SOCKET_SHUTDOWN_READ = 0,
- ENET_SOCKET_SHUTDOWN_WRITE = 1,
- ENET_SOCKET_SHUTDOWN_READ_WRITE = 2
-} ENetSocketShutdown;
-
-#define ENET_HOST_ANY 0
-#define ENET_HOST_BROADCAST 0xFFFFFFFFU
-#define ENET_PORT_ANY 0
-
-/**
- * Portable internet address structure.
- *
- * The host must be specified in network byte-order, and the port must be in host
- * byte-order. The constant ENET_HOST_ANY may be used to specify the default
- * server host. The constant ENET_HOST_BROADCAST may be used to specify the
- * broadcast address (255.255.255.255). This makes sense for enet_host_connect,
- * but not for enet_host_create. Once a server responds to a broadcast, the
- * address is updated from ENET_HOST_BROADCAST to the server's actual IP address.
- */
-typedef struct _ENetAddress
-{
- enet_uint32 host;
- enet_uint16 port;
-} ENetAddress;
-
-/**
- * Packet flag bit constants.
- *
- * The host must be specified in network byte-order, and the port must be in
- * host byte-order. The constant ENET_HOST_ANY may be used to specify the
- * default server host.
-
- @sa ENetPacket
-*/
-typedef enum _ENetPacketFlag
-{
- /** packet must be received by the target peer and resend attempts should be
- * made until the packet is delivered */
- ENET_PACKET_FLAG_RELIABLE = (1 << 0),
- /** packet will not be sequenced with other packets
- * not supported for reliable packets
- */
- ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1),
- /** packet will not allocate data, and user must supply it instead */
- ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2),
- /** packet will be fragmented using unreliable (instead of reliable) sends
- * if it exceeds the MTU */
- ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3),
-
- /** whether the packet has been sent from all queues it has been entered into */
- ENET_PACKET_FLAG_SENT = (1<<8)
-} ENetPacketFlag;
-
-typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *);
-
-/**
- * ENet packet structure.
- *
- * An ENet data packet that may be sent to or received from a peer. The shown
- * fields should only be read and never modified. The data field contains the
- * allocated data for the packet. The dataLength fields specifies the length
- * of the allocated data. The flags field is either 0 (specifying no flags),
- * or a bitwise-or of any combination of the following flags:
- *
- * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer
- * and resend attempts should be made until the packet is delivered
- *
- * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets
- * (not supported for reliable packets)
- *
- * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead
- *
- * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable
- * (instead of reliable) sends if it exceeds the MTU
- *
- * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into
- @sa ENetPacketFlag
- */
-typedef struct _ENetPacket
-{
- size_t referenceCount; /**< internal use only */
- enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */
- enet_uint8 * data; /**< allocated data for packet */
- size_t dataLength; /**< length of data */
- ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */
- void * userData; /**< application private data, may be freely modified */
-} ENetPacket;
-
-typedef struct _ENetAcknowledgement
-{
- ENetListNode acknowledgementList;
- enet_uint32 sentTime;
- ENetProtocol command;
-} ENetAcknowledgement;
-
-typedef struct _ENetOutgoingCommand
-{
- ENetListNode outgoingCommandList;
- enet_uint16 reliableSequenceNumber;
- enet_uint16 unreliableSequenceNumber;
- enet_uint32 sentTime;
- enet_uint32 roundTripTimeout;
- enet_uint32 roundTripTimeoutLimit;
- enet_uint32 fragmentOffset;
- enet_uint16 fragmentLength;
- enet_uint16 sendAttempts;
- ENetProtocol command;
- ENetPacket * packet;
-} ENetOutgoingCommand;
-
-typedef struct _ENetIncomingCommand
-{
- ENetListNode incomingCommandList;
- enet_uint16 reliableSequenceNumber;
- enet_uint16 unreliableSequenceNumber;
- ENetProtocol command;
- enet_uint32 fragmentCount;
- enet_uint32 fragmentsRemaining;
- enet_uint32 * fragments;
- ENetPacket * packet;
-} ENetIncomingCommand;
-
-typedef enum _ENetPeerState
-{
- ENET_PEER_STATE_DISCONNECTED = 0,
- ENET_PEER_STATE_CONNECTING = 1,
- ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
- ENET_PEER_STATE_CONNECTION_PENDING = 3,
- ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4,
- ENET_PEER_STATE_CONNECTED = 5,
- ENET_PEER_STATE_DISCONNECT_LATER = 6,
- ENET_PEER_STATE_DISCONNECTING = 7,
- ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8,
- ENET_PEER_STATE_ZOMBIE = 9
-} ENetPeerState;
-
-#ifndef ENET_BUFFER_MAXIMUM
-#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
-#endif
-
-enum
-{
- ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024,
- ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024,
- ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000,
- ENET_HOST_DEFAULT_MTU = 1400,
- ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024,
- ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024,
-
- ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500,
- ENET_PEER_DEFAULT_PACKET_THROTTLE = 32,
- ENET_PEER_PACKET_THROTTLE_SCALE = 32,
- ENET_PEER_PACKET_THROTTLE_COUNTER = 7,
- ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2,
- ENET_PEER_PACKET_THROTTLE_DECELERATION = 2,
- ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000,
- ENET_PEER_PACKET_LOSS_SCALE = (1 << 16),
- ENET_PEER_PACKET_LOSS_INTERVAL = 10000,
- ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024,
- ENET_PEER_TIMEOUT_LIMIT = 32,
- ENET_PEER_TIMEOUT_MINIMUM = 5000,
- ENET_PEER_TIMEOUT_MAXIMUM = 30000,
- ENET_PEER_PING_INTERVAL = 500,
- ENET_PEER_UNSEQUENCED_WINDOWS = 64,
- ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024,
- ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32,
- ENET_PEER_RELIABLE_WINDOWS = 16,
- ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000,
- ENET_PEER_FREE_RELIABLE_WINDOWS = 8
-};
-
-typedef struct _ENetChannel
-{
- enet_uint16 outgoingReliableSequenceNumber;
- enet_uint16 outgoingUnreliableSequenceNumber;
- enet_uint16 usedReliableWindows;
- enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS];
- enet_uint16 incomingReliableSequenceNumber;
- enet_uint16 incomingUnreliableSequenceNumber;
- ENetList incomingReliableCommands;
- ENetList incomingUnreliableCommands;
-} ENetChannel;
-
-/**
- * An ENet peer which data packets may be sent or received from.
- *
- * No fields should be modified unless otherwise specified.
- */
-typedef struct _ENetPeer
-{
- ENetListNode dispatchList;
- struct _ENetHost * host;
- enet_uint16 outgoingPeerID;
- enet_uint16 incomingPeerID;
- enet_uint32 connectID;
- enet_uint8 outgoingSessionID;
- enet_uint8 incomingSessionID;
- ENetAddress address; /**< Internet address of the peer */
- void * data; /**< Application private data, may be freely modified */
- ENetPeerState state;
- ENetChannel * channels;
- size_t channelCount; /**< Number of channels allocated for communication with peer */
- enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */
- enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */
- enet_uint32 incomingBandwidthThrottleEpoch;
- enet_uint32 outgoingBandwidthThrottleEpoch;
- enet_uint32 incomingDataTotal;
- enet_uint32 outgoingDataTotal;
- enet_uint32 lastSendTime;
- enet_uint32 lastReceiveTime;
- enet_uint32 nextTimeout;
- enet_uint32 earliestTimeout;
- enet_uint32 packetLossEpoch;
- enet_uint32 packetsSent;
- enet_uint32 packetsLost;
- enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */
- enet_uint32 packetLossVariance;
- enet_uint32 packetThrottle;
- enet_uint32 packetThrottleLimit;
- enet_uint32 packetThrottleCounter;
- enet_uint32 packetThrottleEpoch;
- enet_uint32 packetThrottleAcceleration;
- enet_uint32 packetThrottleDeceleration;
- enet_uint32 packetThrottleInterval;
- enet_uint32 pingInterval;
- enet_uint32 timeoutLimit;
- enet_uint32 timeoutMinimum;
- enet_uint32 timeoutMaximum;
- enet_uint32 lastRoundTripTime;
- enet_uint32 lowestRoundTripTime;
- enet_uint32 lastRoundTripTimeVariance;
- enet_uint32 highestRoundTripTimeVariance;
- enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */
- enet_uint32 roundTripTimeVariance;
- enet_uint32 mtu;
- enet_uint32 windowSize;
- enet_uint32 reliableDataInTransit;
- enet_uint16 outgoingReliableSequenceNumber;
- ENetList acknowledgements;
- ENetList sentReliableCommands;
- ENetList sentUnreliableCommands;
- ENetList outgoingReliableCommands;
- ENetList outgoingUnreliableCommands;
- ENetList dispatchedCommands;
- int needsDispatch;
- enet_uint16 incomingUnsequencedGroup;
- enet_uint16 outgoingUnsequencedGroup;
- enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32];
- enet_uint32 eventData;
- size_t totalWaitingData;
-} ENetPeer;
-
-/** An ENet packet compressor for compressing UDP packets before socket sends or receives.
- */
-typedef struct _ENetCompressor
-{
- /** Context data for the compressor. Must be non-NULL. */
- void * context;
- /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
- size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
- /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */
- size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
- /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */
- void (ENET_CALLBACK * destroy) (void * context);
-} ENetCompressor;
-
-/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */
-typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount);
-
-/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */
-typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event);
-
-/** An ENet host for communicating with peers.
- *
- * No fields should be modified unless otherwise stated.
-
- @sa enet_host_create()
- @sa enet_host_destroy()
- @sa enet_host_connect()
- @sa enet_host_service()
- @sa enet_host_flush()
- @sa enet_host_broadcast()
- @sa enet_host_compress()
- @sa enet_host_compress_with_range_coder()
- @sa enet_host_channel_limit()
- @sa enet_host_bandwidth_limit()
- @sa enet_host_bandwidth_throttle()
- */
-typedef struct _ENetHost
-{
- ENetSocket socket;
- ENetAddress address; /**< Internet address of the host */
- enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */
- enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */
- enet_uint32 bandwidthThrottleEpoch;
- enet_uint32 mtu;
- enet_uint32 randomSeed;
- int recalculateBandwidthLimits;
- ENetPeer * peers; /**< array of peers allocated for this host */
- size_t peerCount; /**< number of peers allocated for this host */
- size_t channelLimit; /**< maximum number of channels allowed for connected peers */
- enet_uint32 serviceTime;
- ENetList dispatchQueue;
- int continueSending;
- size_t packetSize;
- enet_uint16 headerFlags;
- ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS];
- size_t commandCount;
- ENetBuffer buffers [ENET_BUFFER_MAXIMUM];
- size_t bufferCount;
- ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */
- ENetCompressor compressor;
- enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU];
- ENetAddress receivedAddress;
- enet_uint8 * receivedData;
- size_t receivedDataLength;
- enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */
- enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */
- enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */
- enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */
- ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */
- size_t connectedPeers;
- size_t bandwidthLimitedPeers;
- size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */
- size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */
- size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */
-} ENetHost;
-
-/**
- * An ENet event type, as specified in @ref ENetEvent.
- */
-typedef enum _ENetEventType
-{
- /** no event occurred within the specified time limit */
- ENET_EVENT_TYPE_NONE = 0,
-
- /** a connection request initiated by enet_host_connect has completed.
- * The peer field contains the peer which successfully connected.
- */
- ENET_EVENT_TYPE_CONNECT = 1,
-
- /** a peer has disconnected. This event is generated on a successful
- * completion of a disconnect initiated by enet_peer_disconnect, if
- * a peer has timed out, or if a connection request intialized by
- * enet_host_connect has timed out. The peer field contains the peer
- * which disconnected. The data field contains user supplied data
- * describing the disconnection, or 0, if none is available.
- */
- ENET_EVENT_TYPE_DISCONNECT = 2,
-
- /** a packet has been received from a peer. The peer field specifies the
- * peer which sent the packet. The channelID field specifies the channel
- * number upon which the packet was received. The packet field contains
- * the packet that was received; this packet must be destroyed with
- * enet_packet_destroy after use.
- */
- ENET_EVENT_TYPE_RECEIVE = 3
-} ENetEventType;
-
-/**
- * An ENet event as returned by enet_host_service().
-
- @sa enet_host_service
- */
-typedef struct _ENetEvent
-{
- ENetEventType type; /**< type of the event */
- ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */
- enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */
- enet_uint32 data; /**< data associated with the event, if appropriate */
- ENetPacket * packet; /**< packet associated with the event, if appropriate */
-} ENetEvent;
-
-/** @defgroup global ENet global functions
- @{
-*/
-
-/**
- Initializes ENet globally. Must be called prior to using any functions in
- ENet.
- @returns 0 on success, < 0 on failure
-*/
-ENET_API int enet_initialize (void);
-
-/**
- Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored.
-
- @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use
- @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults
- @returns 0 on success, < 0 on failure
-*/
-ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits);
-
-/**
- Shuts down ENet globally. Should be called when a program that has
- initialized ENet exits.
-*/
-ENET_API void enet_deinitialize (void);
-
-/**
- Gives the linked version of the ENet library.
- @returns the version number
-*/
-ENET_API ENetVersion enet_linked_version (void);
-
-/** @} */
-
-/** @defgroup private ENet private implementation functions */
-
-/**
- Returns the wall-time in milliseconds. Its initial value is unspecified
- unless otherwise set.
- */
-ENET_API enet_uint32 enet_time_get (void);
-/**
- Sets the current wall-time in milliseconds.
- */
-ENET_API void enet_time_set (enet_uint32);
-
-/** @defgroup socket ENet socket functions
- @{
-*/
-ENET_API ENetSocket enet_socket_create (ENetSocketType);
-ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *);
-ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *);
-ENET_API int enet_socket_listen (ENetSocket, int);
-ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *);
-ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *);
-ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t);
-ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t);
-ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32);
-ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int);
-ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *);
-ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown);
-ENET_API void enet_socket_destroy (ENetSocket);
-ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32);
-
-/** @} */
-
-/** @defgroup Address ENet address functions
- @{
-*/
-/** Attempts to resolve the host named by the parameter hostName and sets
- the host field in the address parameter if successful.
- @param address destination to store resolved address
- @param hostName host name to lookup
- @retval 0 on success
- @retval < 0 on failure
- @returns the address of the given hostName in address on success
-*/
-ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName);
-
-/** Gives the printable form of the IP address specified in the address parameter.
- @param address address printed
- @param hostName destination for name, must not be NULL
- @param nameLength maximum length of hostName.
- @returns the null-terminated name of the host in hostName on success
- @retval 0 on success
- @retval < 0 on failure
-*/
-ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength);
-
-/** Attempts to do a reverse lookup of the host field in the address parameter.
- @param address address used for reverse lookup
- @param hostName destination for name, must not be NULL
- @param nameLength maximum length of hostName.
- @returns the null-terminated name of the host in hostName on success
- @retval 0 on success
- @retval < 0 on failure
-*/
-ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength);
-
-/** @} */
-
-ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32);
-ENET_API void enet_packet_destroy (ENetPacket *);
-ENET_API int enet_packet_resize (ENetPacket *, size_t);
-ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t);
-
-ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32);
-ENET_API void enet_host_destroy (ENetHost *);
-ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32);
-ENET_API int enet_host_check_events (ENetHost *, ENetEvent *);
-ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32);
-ENET_API void enet_host_flush (ENetHost *);
-ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *);
-ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *);
-ENET_API int enet_host_compress_with_range_coder (ENetHost * host);
-ENET_API void enet_host_channel_limit (ENetHost *, size_t);
-ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32);
-extern void enet_host_bandwidth_throttle (ENetHost *);
-extern enet_uint32 enet_host_random_seed (void);
-
-ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *);
-ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID);
-ENET_API void enet_peer_ping (ENetPeer *);
-ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32);
-ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
-ENET_API void enet_peer_reset (ENetPeer *);
-ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32);
-ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32);
-ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32);
-ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32);
-extern int enet_peer_throttle (ENetPeer *, enet_uint32);
-extern void enet_peer_reset_queues (ENetPeer *);
-extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *);
-extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16);
-extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32);
-extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16);
-extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *);
-extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *);
-extern void enet_peer_on_connect (ENetPeer *);
-extern void enet_peer_on_disconnect (ENetPeer *);
-
-ENET_API void * enet_range_coder_create (void);
-ENET_API void enet_range_coder_destroy (void *);
-ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t);
-ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t);
-
-extern size_t enet_protocol_command_size (enet_uint8);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __ENET_ENET_H__ */
-
diff --git a/modules/enet/enet/list.h b/modules/enet/enet/list.h
deleted file mode 100644
index d7b2600848..0000000000
--- a/modules/enet/enet/list.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- @file list.h
- @brief ENet list management
-*/
-#ifndef __ENET_LIST_H__
-#define __ENET_LIST_H__
-
-#include <stdlib.h>
-
-typedef struct _ENetListNode
-{
- struct _ENetListNode * next;
- struct _ENetListNode * previous;
-} ENetListNode;
-
-typedef ENetListNode * ENetListIterator;
-
-typedef struct _ENetList
-{
- ENetListNode sentinel;
-} ENetList;
-
-extern void enet_list_clear (ENetList *);
-
-extern ENetListIterator enet_list_insert (ENetListIterator, void *);
-extern void * enet_list_remove (ENetListIterator);
-extern ENetListIterator enet_list_move (ENetListIterator, void *, void *);
-
-extern size_t enet_list_size (ENetList *);
-
-#define enet_list_begin(list) ((list) -> sentinel.next)
-#define enet_list_end(list) (& (list) -> sentinel)
-
-#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
-
-#define enet_list_next(iterator) ((iterator) -> next)
-#define enet_list_previous(iterator) ((iterator) -> previous)
-
-#define enet_list_front(list) ((void *) (list) -> sentinel.next)
-#define enet_list_back(list) ((void *) (list) -> sentinel.previous)
-
-#endif /* __ENET_LIST_H__ */
-
diff --git a/modules/enet/enet/protocol.h b/modules/enet/enet/protocol.h
deleted file mode 100644
index f8c73d8a66..0000000000
--- a/modules/enet/enet/protocol.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/**
- @file protocol.h
- @brief ENet protocol
-*/
-#ifndef __ENET_PROTOCOL_H__
-#define __ENET_PROTOCOL_H__
-
-#include "enet/types.h"
-
-enum
-{
- ENET_PROTOCOL_MINIMUM_MTU = 576,
- ENET_PROTOCOL_MAXIMUM_MTU = 4096,
- ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32,
- ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096,
- ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536,
- ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1,
- ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255,
- ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF,
- ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024
-};
-
-typedef enum _ENetProtocolCommand
-{
- ENET_PROTOCOL_COMMAND_NONE = 0,
- ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1,
- ENET_PROTOCOL_COMMAND_CONNECT = 2,
- ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3,
- ENET_PROTOCOL_COMMAND_DISCONNECT = 4,
- ENET_PROTOCOL_COMMAND_PING = 5,
- ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6,
- ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7,
- ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8,
- ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9,
- ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10,
- ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
- ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12,
- ENET_PROTOCOL_COMMAND_COUNT = 13,
-
- ENET_PROTOCOL_COMMAND_MASK = 0x0F
-} ENetProtocolCommand;
-
-typedef enum _ENetProtocolFlag
-{
- ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7),
- ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6),
-
- ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14),
- ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15),
- ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME,
-
- ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12),
- ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12
-} ENetProtocolFlag;
-
-#ifdef _MSC_VER
-#pragma pack(push, 1)
-#define ENET_PACKED
-#elif defined(__GNUC__) || defined(__clang__)
-#define ENET_PACKED __attribute__ ((packed))
-#else
-#define ENET_PACKED
-#endif
-
-typedef struct _ENetProtocolHeader
-{
- enet_uint16 peerID;
- enet_uint16 sentTime;
-} ENET_PACKED ENetProtocolHeader;
-
-typedef struct _ENetProtocolCommandHeader
-{
- enet_uint8 command;
- enet_uint8 channelID;
- enet_uint16 reliableSequenceNumber;
-} ENET_PACKED ENetProtocolCommandHeader;
-
-typedef struct _ENetProtocolAcknowledge
-{
- ENetProtocolCommandHeader header;
- enet_uint16 receivedReliableSequenceNumber;
- enet_uint16 receivedSentTime;
-} ENET_PACKED ENetProtocolAcknowledge;
-
-typedef struct _ENetProtocolConnect
-{
- ENetProtocolCommandHeader header;
- enet_uint16 outgoingPeerID;
- enet_uint8 incomingSessionID;
- enet_uint8 outgoingSessionID;
- enet_uint32 mtu;
- enet_uint32 windowSize;
- enet_uint32 channelCount;
- enet_uint32 incomingBandwidth;
- enet_uint32 outgoingBandwidth;
- enet_uint32 packetThrottleInterval;
- enet_uint32 packetThrottleAcceleration;
- enet_uint32 packetThrottleDeceleration;
- enet_uint32 connectID;
- enet_uint32 data;
-} ENET_PACKED ENetProtocolConnect;
-
-typedef struct _ENetProtocolVerifyConnect
-{
- ENetProtocolCommandHeader header;
- enet_uint16 outgoingPeerID;
- enet_uint8 incomingSessionID;
- enet_uint8 outgoingSessionID;
- enet_uint32 mtu;
- enet_uint32 windowSize;
- enet_uint32 channelCount;
- enet_uint32 incomingBandwidth;
- enet_uint32 outgoingBandwidth;
- enet_uint32 packetThrottleInterval;
- enet_uint32 packetThrottleAcceleration;
- enet_uint32 packetThrottleDeceleration;
- enet_uint32 connectID;
-} ENET_PACKED ENetProtocolVerifyConnect;
-
-typedef struct _ENetProtocolBandwidthLimit
-{
- ENetProtocolCommandHeader header;
- enet_uint32 incomingBandwidth;
- enet_uint32 outgoingBandwidth;
-} ENET_PACKED ENetProtocolBandwidthLimit;
-
-typedef struct _ENetProtocolThrottleConfigure
-{
- ENetProtocolCommandHeader header;
- enet_uint32 packetThrottleInterval;
- enet_uint32 packetThrottleAcceleration;
- enet_uint32 packetThrottleDeceleration;
-} ENET_PACKED ENetProtocolThrottleConfigure;
-
-typedef struct _ENetProtocolDisconnect
-{
- ENetProtocolCommandHeader header;
- enet_uint32 data;
-} ENET_PACKED ENetProtocolDisconnect;
-
-typedef struct _ENetProtocolPing
-{
- ENetProtocolCommandHeader header;
-} ENET_PACKED ENetProtocolPing;
-
-typedef struct _ENetProtocolSendReliable
-{
- ENetProtocolCommandHeader header;
- enet_uint16 dataLength;
-} ENET_PACKED ENetProtocolSendReliable;
-
-typedef struct _ENetProtocolSendUnreliable
-{
- ENetProtocolCommandHeader header;
- enet_uint16 unreliableSequenceNumber;
- enet_uint16 dataLength;
-} ENET_PACKED ENetProtocolSendUnreliable;
-
-typedef struct _ENetProtocolSendUnsequenced
-{
- ENetProtocolCommandHeader header;
- enet_uint16 unsequencedGroup;
- enet_uint16 dataLength;
-} ENET_PACKED ENetProtocolSendUnsequenced;
-
-typedef struct _ENetProtocolSendFragment
-{
- ENetProtocolCommandHeader header;
- enet_uint16 startSequenceNumber;
- enet_uint16 dataLength;
- enet_uint32 fragmentCount;
- enet_uint32 fragmentNumber;
- enet_uint32 totalLength;
- enet_uint32 fragmentOffset;
-} ENET_PACKED ENetProtocolSendFragment;
-
-typedef union _ENetProtocol
-{
- ENetProtocolCommandHeader header;
- ENetProtocolAcknowledge acknowledge;
- ENetProtocolConnect connect;
- ENetProtocolVerifyConnect verifyConnect;
- ENetProtocolDisconnect disconnect;
- ENetProtocolPing ping;
- ENetProtocolSendReliable sendReliable;
- ENetProtocolSendUnreliable sendUnreliable;
- ENetProtocolSendUnsequenced sendUnsequenced;
- ENetProtocolSendFragment sendFragment;
- ENetProtocolBandwidthLimit bandwidthLimit;
- ENetProtocolThrottleConfigure throttleConfigure;
-} ENET_PACKED ENetProtocol;
-
-#ifdef _MSC_VER
-#pragma pack(pop)
-#endif
-
-#endif /* __ENET_PROTOCOL_H__ */
-
diff --git a/modules/enet/enet/time.h b/modules/enet/enet/time.h
deleted file mode 100644
index c82a546035..0000000000
--- a/modules/enet/enet/time.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- @file time.h
- @brief ENet time constants and macros
-*/
-#ifndef __ENET_TIME_H__
-#define __ENET_TIME_H__
-
-#define ENET_TIME_OVERFLOW 86400000
-
-#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW)
-#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW)
-#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b))
-#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b))
-
-#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b))
-
-#endif /* __ENET_TIME_H__ */
-
diff --git a/modules/enet/enet/types.h b/modules/enet/enet/types.h
deleted file mode 100644
index ab010a4b13..0000000000
--- a/modules/enet/enet/types.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- @file types.h
- @brief type definitions for ENet
-*/
-#ifndef __ENET_TYPES_H__
-#define __ENET_TYPES_H__
-
-typedef unsigned char enet_uint8; /**< unsigned 8-bit type */
-typedef unsigned short enet_uint16; /**< unsigned 16-bit type */
-typedef unsigned int enet_uint32; /**< unsigned 32-bit type */
-
-#endif /* __ENET_TYPES_H__ */
-
diff --git a/modules/enet/enet/unix.h b/modules/enet/enet/unix.h
deleted file mode 100644
index a59e340606..0000000000
--- a/modules/enet/enet/unix.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- @file unix.h
- @brief ENet Unix header
-*/
-#ifndef __ENET_UNIX_H__
-#define __ENET_UNIX_H__
-
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <unistd.h>
-
-#ifdef MSG_MAXIOVLEN
-#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN
-#endif
-
-typedef int ENetSocket;
-
-#define ENET_SOCKET_NULL -1
-
-#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */
-#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */
-
-#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */
-#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */
-
-typedef struct
-{
- void * data;
- size_t dataLength;
-} ENetBuffer;
-
-#define ENET_CALLBACK
-
-#define ENET_API extern
-
-typedef fd_set ENetSocketSet;
-
-#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
-#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
-#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
-#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
-
-#endif /* __ENET_UNIX_H__ */
-
diff --git a/modules/enet/enet/utility.h b/modules/enet/enet/utility.h
deleted file mode 100644
index e48a476be3..0000000000
--- a/modules/enet/enet/utility.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- @file utility.h
- @brief ENet utility header
-*/
-#ifndef __ENET_UTILITY_H__
-#define __ENET_UTILITY_H__
-
-#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y))
-#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y))
-
-#endif /* __ENET_UTILITY_H__ */
-
diff --git a/modules/enet/enet/win32.h b/modules/enet/enet/win32.h
deleted file mode 100644
index e73ca9d052..0000000000
--- a/modules/enet/enet/win32.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- @file win32.h
- @brief ENet Win32 header
-*/
-#ifndef __ENET_WIN32_H__
-#define __ENET_WIN32_H__
-
-#ifdef _MSC_VER
-#ifdef ENET_BUILDING_LIB
-#pragma warning (disable: 4267) // size_t to int conversion
-#pragma warning (disable: 4244) // 64bit to 32bit int
-#pragma warning (disable: 4018) // signed/unsigned mismatch
-#pragma warning (disable: 4146) // unary minus operator applied to unsigned type
-#endif
-#endif
-
-#include <stdlib.h>
-#include <winsock2.h>
-
-typedef SOCKET ENetSocket;
-
-#define ENET_SOCKET_NULL INVALID_SOCKET
-
-#define ENET_HOST_TO_NET_16(value) (htons (value))
-#define ENET_HOST_TO_NET_32(value) (htonl (value))
-
-#define ENET_NET_TO_HOST_16(value) (ntohs (value))
-#define ENET_NET_TO_HOST_32(value) (ntohl (value))
-
-typedef struct
-{
- size_t dataLength;
- void * data;
-} ENetBuffer;
-
-#define ENET_CALLBACK __cdecl
-
-#ifdef ENET_DLL
-#ifdef ENET_BUILDING_LIB
-#define ENET_API __declspec( dllexport )
-#else
-#define ENET_API __declspec( dllimport )
-#endif /* ENET_BUILDING_LIB */
-#else /* !ENET_DLL */
-#define ENET_API extern
-#endif /* ENET_DLL */
-
-typedef fd_set ENetSocketSet;
-
-#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset))
-#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset))
-#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset))
-#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset))
-
-#endif /* __ENET_WIN32_H__ */
-
-
diff --git a/modules/enet/host.c b/modules/enet/host.c
deleted file mode 100644
index 3be6c0922c..0000000000
--- a/modules/enet/host.c
+++ /dev/null
@@ -1,492 +0,0 @@
-/**
- @file host.c
- @brief ENet host management functions
-*/
-#define ENET_BUILDING_LIB 1
-#include <string.h>
-#include "enet/enet.h"
-
-/** @defgroup host ENet host functions
- @{
-*/
-
-/** Creates a host for communicating to peers.
-
- @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host.
- @param peerCount the maximum number of peers that should be allocated for the host.
- @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
- @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
- @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
-
- @returns the host on success and NULL on failure
-
- @remarks ENet will strategically drop packets on specific sides of a connection between hosts
- to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine
- the window size of a connection which limits the amount of reliable packets that may be in transit
- at any given time.
-*/
-ENetHost *
-enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
-{
- ENetHost * host;
- ENetPeer * currentPeer;
-
- if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
- return NULL;
-
- host = (ENetHost *) enet_malloc (sizeof (ENetHost));
- if (host == NULL)
- return NULL;
- memset (host, 0, sizeof (ENetHost));
-
- host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));
- if (host -> peers == NULL)
- {
- enet_free (host);
-
- return NULL;
- }
- memset (host -> peers, 0, peerCount * sizeof (ENetPeer));
-
- host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
- if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
- {
- if (host -> socket != ENET_SOCKET_NULL)
- enet_socket_destroy (host -> socket);
-
- enet_free (host -> peers);
- enet_free (host);
-
- return NULL;
- }
-
- enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
- enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
- enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
- enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
-
- if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)
- host -> address = * address;
-
- if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
- else
- if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
-
- host -> randomSeed = (enet_uint32) (size_t) host;
- host -> randomSeed += enet_host_random_seed ();
- host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
- host -> channelLimit = channelLimit;
- host -> incomingBandwidth = incomingBandwidth;
- host -> outgoingBandwidth = outgoingBandwidth;
- host -> bandwidthThrottleEpoch = 0;
- host -> recalculateBandwidthLimits = 0;
- host -> mtu = ENET_HOST_DEFAULT_MTU;
- host -> peerCount = peerCount;
- host -> commandCount = 0;
- host -> bufferCount = 0;
- host -> checksum = NULL;
- host -> receivedAddress.host = ENET_HOST_ANY;
- host -> receivedAddress.port = 0;
- host -> receivedData = NULL;
- host -> receivedDataLength = 0;
-
- host -> totalSentData = 0;
- host -> totalSentPackets = 0;
- host -> totalReceivedData = 0;
- host -> totalReceivedPackets = 0;
-
- host -> connectedPeers = 0;
- host -> bandwidthLimitedPeers = 0;
- host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
- host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
- host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
-
- host -> compressor.context = NULL;
- host -> compressor.compress = NULL;
- host -> compressor.decompress = NULL;
- host -> compressor.destroy = NULL;
-
- host -> intercept = NULL;
-
- enet_list_clear (& host -> dispatchQueue);
-
- for (currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- currentPeer -> host = host;
- currentPeer -> incomingPeerID = currentPeer - host -> peers;
- currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
- currentPeer -> data = NULL;
-
- enet_list_clear (& currentPeer -> acknowledgements);
- enet_list_clear (& currentPeer -> sentReliableCommands);
- enet_list_clear (& currentPeer -> sentUnreliableCommands);
- enet_list_clear (& currentPeer -> outgoingReliableCommands);
- enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
- enet_list_clear (& currentPeer -> dispatchedCommands);
-
- enet_peer_reset (currentPeer);
- }
-
- return host;
-}
-
-/** Destroys the host and all resources associated with it.
- @param host pointer to the host to destroy
-*/
-void
-enet_host_destroy (ENetHost * host)
-{
- ENetPeer * currentPeer;
-
- if (host == NULL)
- return;
-
- enet_socket_destroy (host -> socket);
-
- for (currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- enet_peer_reset (currentPeer);
- }
-
- if (host -> compressor.context != NULL && host -> compressor.destroy)
- (* host -> compressor.destroy) (host -> compressor.context);
-
- enet_free (host -> peers);
- enet_free (host);
-}
-
-/** Initiates a connection to a foreign host.
- @param host host seeking the connection
- @param address destination for the connection
- @param channelCount number of channels to allocate
- @param data user data supplied to the receiving host
- @returns a peer representing the foreign host on success, NULL on failure
- @remarks The peer returned will have not completed the connection until enet_host_service()
- notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
-*/
-ENetPeer *
-enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
-{
- ENetPeer * currentPeer;
- ENetChannel * channel;
- ENetProtocol command;
-
- if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
- channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
- else
- if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
-
- for (currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
- break;
- }
-
- if (currentPeer >= & host -> peers [host -> peerCount])
- return NULL;
-
- currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
- if (currentPeer -> channels == NULL)
- return NULL;
- currentPeer -> channelCount = channelCount;
- currentPeer -> state = ENET_PEER_STATE_CONNECTING;
- currentPeer -> address = * address;
- currentPeer -> connectID = ++ host -> randomSeed;
-
- if (host -> outgoingBandwidth == 0)
- currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
- else
- currentPeer -> windowSize = (host -> outgoingBandwidth /
- ENET_PEER_WINDOW_SIZE_SCALE) *
- ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
-
- if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
- currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
- currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
-
- for (channel = currentPeer -> channels;
- channel < & currentPeer -> channels [channelCount];
- ++ channel)
- {
- channel -> outgoingReliableSequenceNumber = 0;
- channel -> outgoingUnreliableSequenceNumber = 0;
- channel -> incomingReliableSequenceNumber = 0;
- channel -> incomingUnreliableSequenceNumber = 0;
-
- enet_list_clear (& channel -> incomingReliableCommands);
- enet_list_clear (& channel -> incomingUnreliableCommands);
-
- channel -> usedReliableWindows = 0;
- memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
- }
-
- command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
- command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID);
- command.connect.incomingSessionID = currentPeer -> incomingSessionID;
- command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
- command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
- command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
- command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
- command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
- command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
- command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
- command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
- command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
- command.connect.connectID = currentPeer -> connectID;
- command.connect.data = ENET_HOST_TO_NET_32 (data);
-
- enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
-
- return currentPeer;
-}
-
-/** Queues a packet to be sent to all peers associated with the host.
- @param host host on which to broadcast the packet
- @param channelID channel on which to broadcast
- @param packet packet to broadcast
-*/
-void
-enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
-{
- ENetPeer * currentPeer;
-
- for (currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
- continue;
-
- enet_peer_send (currentPeer, channelID, packet);
- }
-
- if (packet -> referenceCount == 0)
- enet_packet_destroy (packet);
-}
-
-/** Sets the packet compressor the host should use to compress and decompress packets.
- @param host host to enable or disable compression for
- @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
-*/
-void
-enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
-{
- if (host -> compressor.context != NULL && host -> compressor.destroy)
- (* host -> compressor.destroy) (host -> compressor.context);
-
- if (compressor)
- host -> compressor = * compressor;
- else
- host -> compressor.context = NULL;
-}
-
-/** Limits the maximum allowed channels of future incoming connections.
- @param host host to limit
- @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
-*/
-void
-enet_host_channel_limit (ENetHost * host, size_t channelLimit)
-{
- if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
- else
- if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
- channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
-
- host -> channelLimit = channelLimit;
-}
-
-
-/** Adjusts the bandwidth limits of a host.
- @param host host to adjust
- @param incomingBandwidth new incoming bandwidth
- @param outgoingBandwidth new outgoing bandwidth
- @remarks the incoming and outgoing bandwidth parameters are identical in function to those
- specified in enet_host_create().
-*/
-void
-enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
-{
- host -> incomingBandwidth = incomingBandwidth;
- host -> outgoingBandwidth = outgoingBandwidth;
- host -> recalculateBandwidthLimits = 1;
-}
-
-void
-enet_host_bandwidth_throttle (ENetHost * host)
-{
- enet_uint32 timeCurrent = enet_time_get (),
- elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
- peersRemaining = (enet_uint32) host -> connectedPeers,
- dataTotal = ~0,
- bandwidth = ~0,
- throttle = 0,
- bandwidthLimit = 0;
- int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
- ENetPeer * peer;
- ENetProtocol command;
-
- if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
- return;
-
- host -> bandwidthThrottleEpoch = timeCurrent;
-
- if (peersRemaining == 0)
- return;
-
- if (host -> outgoingBandwidth != 0)
- {
- dataTotal = 0;
- bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
-
- for (peer = host -> peers;
- peer < & host -> peers [host -> peerCount];
- ++ peer)
- {
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- continue;
-
- dataTotal += peer -> outgoingDataTotal;
- }
- }
-
- while (peersRemaining > 0 && needsAdjustment != 0)
- {
- needsAdjustment = 0;
-
- if (dataTotal <= bandwidth)
- throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
- else
- throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
-
- for (peer = host -> peers;
- peer < & host -> peers [host -> peerCount];
- ++ peer)
- {
- enet_uint32 peerBandwidth;
-
- if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
- peer -> incomingBandwidth == 0 ||
- peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
- continue;
-
- peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
- if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
- continue;
-
- peer -> packetThrottleLimit = (peerBandwidth *
- ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
-
- if (peer -> packetThrottleLimit == 0)
- peer -> packetThrottleLimit = 1;
-
- if (peer -> packetThrottle > peer -> packetThrottleLimit)
- peer -> packetThrottle = peer -> packetThrottleLimit;
-
- peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
-
- peer -> incomingDataTotal = 0;
- peer -> outgoingDataTotal = 0;
-
- needsAdjustment = 1;
- -- peersRemaining;
- bandwidth -= peerBandwidth;
- dataTotal -= peerBandwidth;
- }
- }
-
- if (peersRemaining > 0)
- {
- if (dataTotal <= bandwidth)
- throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
- else
- throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
-
- for (peer = host -> peers;
- peer < & host -> peers [host -> peerCount];
- ++ peer)
- {
- if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
- peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
- continue;
-
- peer -> packetThrottleLimit = throttle;
-
- if (peer -> packetThrottle > peer -> packetThrottleLimit)
- peer -> packetThrottle = peer -> packetThrottleLimit;
-
- peer -> incomingDataTotal = 0;
- peer -> outgoingDataTotal = 0;
- }
- }
-
- if (host -> recalculateBandwidthLimits)
- {
- host -> recalculateBandwidthLimits = 0;
-
- peersRemaining = (enet_uint32) host -> connectedPeers;
- bandwidth = host -> incomingBandwidth;
- needsAdjustment = 1;
-
- if (bandwidth == 0)
- bandwidthLimit = 0;
- else
- while (peersRemaining > 0 && needsAdjustment != 0)
- {
- needsAdjustment = 0;
- bandwidthLimit = bandwidth / peersRemaining;
-
- for (peer = host -> peers;
- peer < & host -> peers [host -> peerCount];
- ++ peer)
- {
- if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
- peer -> incomingBandwidthThrottleEpoch == timeCurrent)
- continue;
-
- if (peer -> outgoingBandwidth > 0 &&
- peer -> outgoingBandwidth >= bandwidthLimit)
- continue;
-
- peer -> incomingBandwidthThrottleEpoch = timeCurrent;
-
- needsAdjustment = 1;
- -- peersRemaining;
- bandwidth -= peer -> outgoingBandwidth;
- }
- }
-
- for (peer = host -> peers;
- peer < & host -> peers [host -> peerCount];
- ++ peer)
- {
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- continue;
-
- command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
- command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
-
- if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
- command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
- else
- command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
-
- enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
- }
- }
-}
-
-/** @} */
diff --git a/modules/enet/list.c b/modules/enet/list.c
deleted file mode 100644
index 1c1a8dfaaf..0000000000
--- a/modules/enet/list.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- @file list.c
- @brief ENet linked list functions
-*/
-#define ENET_BUILDING_LIB 1
-#include "enet/enet.h"
-
-/**
- @defgroup list ENet linked list utility functions
- @ingroup private
- @{
-*/
-void
-enet_list_clear (ENetList * list)
-{
- list -> sentinel.next = & list -> sentinel;
- list -> sentinel.previous = & list -> sentinel;
-}
-
-ENetListIterator
-enet_list_insert (ENetListIterator position, void * data)
-{
- ENetListIterator result = (ENetListIterator) data;
-
- result -> previous = position -> previous;
- result -> next = position;
-
- result -> previous -> next = result;
- position -> previous = result;
-
- return result;
-}
-
-void *
-enet_list_remove (ENetListIterator position)
-{
- position -> previous -> next = position -> next;
- position -> next -> previous = position -> previous;
-
- return position;
-}
-
-ENetListIterator
-enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast)
-{
- ENetListIterator first = (ENetListIterator) dataFirst,
- last = (ENetListIterator) dataLast;
-
- first -> previous -> next = last -> next;
- last -> next -> previous = first -> previous;
-
- first -> previous = position -> previous;
- last -> next = position;
-
- first -> previous -> next = first;
- position -> previous = last;
-
- return first;
-}
-
-size_t
-enet_list_size (ENetList * list)
-{
- size_t size = 0;
- ENetListIterator position;
-
- for (position = enet_list_begin (list);
- position != enet_list_end (list);
- position = enet_list_next (position))
- ++ size;
-
- return size;
-}
-
-/** @} */
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index 18a4347edf..a82283591d 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -1,3 +1,31 @@
+/*************************************************************************/
+/* networked_multiplayer_enet.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
#include "os/os.h"
#include "io/marshalls.h"
#include "networked_multiplayer_enet.h"
@@ -32,7 +60,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
host = enet_host_create (& address /* the address to bind the server host to */,
p_max_clients /* allow up to 32 clients and/or outgoing connections */,
- 2 /* allow up to 2 channels to be used, 0 and 1 */,
+ SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
p_in_bandwidth /* assume any amount of incoming bandwidth */,
p_out_bandwidth /* assume any amount of outgoing bandwidth */);
@@ -49,10 +77,11 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth){
ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_ip.type != IP_Address::TYPE_IPV4, ERR_INVALID_PARAMETER);
host = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
- 2 /* allow up 2 channels to be used, 0 and 1 */,
+ SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */,
p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */);
@@ -62,7 +91,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
_setup_compressor();
ENetAddress address;
- address.host=p_ip.host;
+ address.host=p_ip.field32[0];
address.port=p_port;
//enet_address_set_host (& address, "localhost");
@@ -70,8 +99,8 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
unique_id=_gen_unique_id();
- /* Initiate the connection, allocating the two channels 0 and 1. */
- ENetPeer *peer = enet_host_connect (host, & address, 2, unique_id);
+ /* Initiate the connection, allocating the enough channels */
+ ENetPeer *peer = enet_host_connect (host, & address, SYSCH_MAX, unique_id);
if (peer == NULL) {
enet_host_destroy(host);
@@ -121,7 +150,8 @@ void NetworkedMultiplayerENet::poll(){
}
IP_Address ip;
- ip.host=event.peer -> address.host;
+ ip.type = IP_Address::TYPE_IPV4;
+ ip.field32[0]=event.peer -> address.host;
int *new_id = memnew( int );
*new_id = event.data;
@@ -148,12 +178,12 @@ void NetworkedMultiplayerENet::poll(){
ENetPacket * packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]);
encode_uint32(E->key(),&packet->data[4]);
- enet_peer_send(event.peer,1,packet);
+ enet_peer_send(event.peer,SYSCH_CONFIG,packet);
//send the new peer to existing peers
packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]);
encode_uint32(*new_id,&packet->data[4]);
- enet_peer_send(E->get(),1,packet);
+ enet_peer_send(E->get(),SYSCH_CONFIG,packet);
}
} else {
@@ -185,7 +215,7 @@ void NetworkedMultiplayerENet::poll(){
ENetPacket* packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_REMOVE_PEER,&packet->data[0]);
encode_uint32(*id,&packet->data[4]);
- enet_peer_send(E->get(),1,packet);
+ enet_peer_send(E->get(),SYSCH_CONFIG,packet);
}
} else if (!server) {
emit_signal("server_disconnected");
@@ -204,10 +234,13 @@ void NetworkedMultiplayerENet::poll(){
case ENET_EVENT_TYPE_RECEIVE: {
- if (event.channelID==1) {
+ if (event.channelID==SYSCH_CONFIG) {
//some config message
ERR_CONTINUE( event.packet->dataLength < 8);
+ // Only server can send config messages
+ ERR_CONTINUE( server );
+
int msg = decode_uint32(&event.packet->data[0]);
int id = decode_uint32(&event.packet->data[4]);
@@ -226,12 +259,12 @@ void NetworkedMultiplayerENet::poll(){
}
enet_packet_destroy(event.packet);
- } else if (event.channelID==0){
+ } else if (event.channelID < SYSCH_MAX){
Packet packet;
packet.packet = event.packet;
- int *id = (int*)event.peer -> data;
+ uint32_t *id = (uint32_t*)event.peer->data;
ERR_CONTINUE(event.packet->dataLength<12)
@@ -243,6 +276,8 @@ void NetworkedMultiplayerENet::poll(){
packet.from=source;
if (server) {
+ // Someone is cheating and trying to fake the source!
+ ERR_CONTINUE(source!=*id);
packet.from=*id;
@@ -258,7 +293,7 @@ void NetworkedMultiplayerENet::poll(){
ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags);
- enet_peer_send(E->get(),0,packet2);
+ enet_peer_send(E->get(),event.channelID,packet2);
}
} else if (target<0) {
@@ -272,7 +307,7 @@ void NetworkedMultiplayerENet::poll(){
ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags);
- enet_peer_send(E->get(),0,packet2);
+ enet_peer_send(E->get(),event.channelID,packet2);
}
if (-target != 1) {
@@ -289,7 +324,7 @@ void NetworkedMultiplayerENet::poll(){
} else {
//to someone else, specifically
ERR_CONTINUE(!peer_map.has(target));
- enet_peer_send(peer_map[target],0,packet.packet);
+ enet_peer_send(peer_map[target],event.channelID,packet.packet);
}
} else {
@@ -359,7 +394,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer,int &r_buffe
incoming_packets.pop_front();
*r_buffer=(const uint8_t*)(&current_packet.packet->data[12]);
- r_buffer_size=current_packet.packet->dataLength;
+ r_buffer_size=current_packet.packet->dataLength-12;
return OK;
}
@@ -369,16 +404,20 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_
ERR_FAIL_COND_V(connection_status!=CONNECTION_CONNECTED,ERR_UNCONFIGURED);
int packet_flags=0;
+ int channel=SYSCH_RELIABLE;
switch(transfer_mode) {
case TRANSFER_MODE_UNRELIABLE: {
packet_flags=ENET_PACKET_FLAG_UNSEQUENCED;
+ channel=SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_UNRELIABLE_ORDERED: {
packet_flags=0;
+ channel=SYSCH_UNRELIABLE;
} break;
case TRANSFER_MODE_RELIABLE: {
packet_flags=ENET_PACKET_FLAG_RELIABLE;
+ channel=SYSCH_RELIABLE;
} break;
}
@@ -402,7 +441,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_
if (server) {
if (target_peer==0) {
- enet_host_broadcast(host,0,packet);
+ enet_host_broadcast(host,channel,packet);
} else if (target_peer<0) {
//send to all but one
//and make copies for sending
@@ -416,18 +455,18 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_
ENetPacket* packet2 = enet_packet_create (packet->data,packet->dataLength,packet_flags);
- enet_peer_send(F->get(),0,packet2);
+ enet_peer_send(F->get(),channel,packet2);
}
enet_packet_destroy(packet); //original packet no longer needed
} else {
- enet_peer_send (E->get(), 0, packet);
+ enet_peer_send (E->get(), channel, packet);
}
} else {
ERR_FAIL_COND_V(!peer_map.has(1),ERR_BUG);
- enet_peer_send (peer_map[1], 0, packet); //send to server for broadcast..
+ enet_peer_send (peer_map[1], channel, packet); //send to server for broadcast..
}
@@ -646,5 +685,6 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){
// sets IP for ENet to bind when using create_server
// if no IP is set, then ENet bind to ENET_HOST_ANY
void NetworkedMultiplayerENet::set_bind_ip(const IP_Address& p_ip){
- bind_ip=p_ip.host;
+ ERR_FAIL_COND(p_ip.type != IP_Address::TYPE_IPV4);
+ bind_ip=p_ip.field32[0];
}
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index 59863c1f78..3db318c96a 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -1,3 +1,31 @@
+/*************************************************************************/
+/* networked_multiplayer_enet.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
#ifndef NETWORKED_MULTIPLAYER_ENET_H
#define NETWORKED_MULTIPLAYER_ENET_H
@@ -23,6 +51,13 @@ private:
SYSMSG_REMOVE_PEER
};
+ enum {
+ SYSCH_CONFIG,
+ SYSCH_RELIABLE,
+ SYSCH_UNRELIABLE,
+ SYSCH_MAX
+ };
+
bool active;
bool server;
diff --git a/modules/enet/packet.c b/modules/enet/packet.c
deleted file mode 100644
index 5fa78b28ae..0000000000
--- a/modules/enet/packet.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- @file packet.c
- @brief ENet packet management functions
-*/
-#include <string.h>
-#define ENET_BUILDING_LIB 1
-#include "enet/enet.h"
-
-/** @defgroup Packet ENet packet functions
- @{
-*/
-
-/** Creates a packet that may be sent to a peer.
- @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL.
- @param dataLength size of the data allocated for this packet
- @param flags flags for this packet as described for the ENetPacket structure.
- @returns the packet on success, NULL on failure
-*/
-ENetPacket *
-enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags)
-{
- ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket));
- if (packet == NULL)
- return NULL;
-
- if (flags & ENET_PACKET_FLAG_NO_ALLOCATE)
- packet -> data = (enet_uint8 *) data;
- else
- if (dataLength <= 0)
- packet -> data = NULL;
- else
- {
- packet -> data = (enet_uint8 *) enet_malloc (dataLength);
- if (packet -> data == NULL)
- {
- enet_free (packet);
- return NULL;
- }
-
- if (data != NULL)
- memcpy (packet -> data, data, dataLength);
- }
-
- packet -> referenceCount = 0;
- packet -> flags = flags;
- packet -> dataLength = dataLength;
- packet -> freeCallback = NULL;
- packet -> userData = NULL;
-
- return packet;
-}
-
-/** Destroys the packet and deallocates its data.
- @param packet packet to be destroyed
-*/
-void
-enet_packet_destroy (ENetPacket * packet)
-{
- if (packet == NULL)
- return;
-
- if (packet -> freeCallback != NULL)
- (* packet -> freeCallback) (packet);
- if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) &&
- packet -> data != NULL)
- enet_free (packet -> data);
- enet_free (packet);
-}
-
-/** Attempts to resize the data in the packet to length specified in the
- dataLength parameter
- @param packet packet to resize
- @param dataLength new size for the packet data
- @returns 0 on success, < 0 on failure
-*/
-int
-enet_packet_resize (ENetPacket * packet, size_t dataLength)
-{
- enet_uint8 * newData;
-
- if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE))
- {
- packet -> dataLength = dataLength;
-
- return 0;
- }
-
- newData = (enet_uint8 *) enet_malloc (dataLength);
- if (newData == NULL)
- return -1;
-
- memcpy (newData, packet -> data, packet -> dataLength);
- enet_free (packet -> data);
-
- packet -> data = newData;
- packet -> dataLength = dataLength;
-
- return 0;
-}
-
-static int initializedCRC32 = 0;
-static enet_uint32 crcTable [256];
-
-static enet_uint32
-reflect_crc (int val, int bits)
-{
- int result = 0, bit;
-
- for (bit = 0; bit < bits; bit ++)
- {
- if(val & 1) result |= 1 << (bits - 1 - bit);
- val >>= 1;
- }
-
- return result;
-}
-
-static void
-initialize_crc32 (void)
-{
- int byte;
-
- for (byte = 0; byte < 256; ++ byte)
- {
- enet_uint32 crc = reflect_crc (byte, 8) << 24;
- int offset;
-
- for(offset = 0; offset < 8; ++ offset)
- {
- if (crc & 0x80000000)
- crc = (crc << 1) ^ 0x04c11db7;
- else
- crc <<= 1;
- }
-
- crcTable [byte] = reflect_crc (crc, 32);
- }
-
- initializedCRC32 = 1;
-}
-
-enet_uint32
-enet_crc32 (const ENetBuffer * buffers, size_t bufferCount)
-{
- enet_uint32 crc = 0xFFFFFFFF;
-
- if (! initializedCRC32) initialize_crc32 ();
-
- while (bufferCount -- > 0)
- {
- const enet_uint8 * data = (const enet_uint8 *) buffers -> data,
- * dataEnd = & data [buffers -> dataLength];
-
- while (data < dataEnd)
- {
- crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++];
- }
-
- ++ buffers;
- }
-
- return ENET_HOST_TO_NET_32 (~ crc);
-}
-
-/** @} */
diff --git a/modules/enet/peer.c b/modules/enet/peer.c
deleted file mode 100644
index e2d0872bd3..0000000000
--- a/modules/enet/peer.c
+++ /dev/null
@@ -1,1004 +0,0 @@
-/**
- @file peer.c
- @brief ENet peer management functions
-*/
-#include <string.h>
-#define ENET_BUILDING_LIB 1
-#include "enet/enet.h"
-
-/** @defgroup peer ENet peer functions
- @{
-*/
-
-/** Configures throttle parameter for a peer.
-
- Unreliable packets are dropped by ENet in response to the varying conditions
- of the Internet connection to the peer. The throttle represents a probability
- that an unreliable packet should not be dropped and thus sent by ENet to the peer.
- The lowest mean round trip time from the sending of a reliable packet to the
- receipt of its acknowledgement is measured over an amount of time specified by
- the interval parameter in milliseconds. If a measured round trip time happens to
- be significantly less than the mean round trip time measured over the interval,
- then the throttle probability is increased to allow more traffic by an amount
- specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE
- constant. If a measured round trip time happens to be significantly greater than
- the mean round trip time measured over the interval, then the throttle probability
- is decreased to limit traffic by an amount specified in the deceleration parameter, which
- is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has
- a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by
- ENet, and so 100% of all unreliable packets will be sent. When the throttle has a
- value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable
- packets will be sent. Intermediate values for the throttle represent intermediate
- probabilities between 0% and 100% of unreliable packets being sent. The bandwidth
- limits of the local and foreign hosts are taken into account to determine a
- sensible limit for the throttle probability above which it should not raise even in
- the best of conditions.
-
- @param peer peer to configure
- @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL.
- @param acceleration rate at which to increase the throttle probability as mean RTT declines
- @param deceleration rate at which to decrease the throttle probability as mean RTT increases
-*/
-void
-enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration)
-{
- ENetProtocol command;
-
- peer -> packetThrottleInterval = interval;
- peer -> packetThrottleAcceleration = acceleration;
- peer -> packetThrottleDeceleration = deceleration;
-
- command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
-
- command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval);
- command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration);
- command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration);
-
- enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
-}
-
-int
-enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt)
-{
- if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance)
- {
- peer -> packetThrottle = peer -> packetThrottleLimit;
- }
- else
- if (rtt < peer -> lastRoundTripTime)
- {
- peer -> packetThrottle += peer -> packetThrottleAcceleration;
-
- if (peer -> packetThrottle > peer -> packetThrottleLimit)
- peer -> packetThrottle = peer -> packetThrottleLimit;
-
- return 1;
- }
- else
- if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
- {
- if (peer -> packetThrottle > peer -> packetThrottleDeceleration)
- peer -> packetThrottle -= peer -> packetThrottleDeceleration;
- else
- peer -> packetThrottle = 0;
-
- return -1;
- }
-
- return 0;
-}
-
-/** Queues a packet to be sent.
- @param peer destination for the packet
- @param channelID channel on which to send
- @param packet packet to send
- @retval 0 on success
- @retval < 0 on failure
-*/
-int
-enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet)
-{
- ENetChannel * channel = & peer -> channels [channelID];
- ENetProtocol command;
- size_t fragmentLength;
-
- if (peer -> state != ENET_PEER_STATE_CONNECTED ||
- channelID >= peer -> channelCount ||
- packet -> dataLength > peer -> host -> maximumPacketSize)
- return -1;
-
- fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment);
- if (peer -> host -> checksum != NULL)
- fragmentLength -= sizeof(enet_uint32);
-
- if (packet -> dataLength > fragmentLength)
- {
- enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength,
- fragmentNumber,
- fragmentOffset;
- enet_uint8 commandNumber;
- enet_uint16 startSequenceNumber;
- ENetList fragments;
- ENetOutgoingCommand * fragment;
-
- if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
- return -1;
-
- if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT &&
- channel -> outgoingUnreliableSequenceNumber < 0xFFFF)
- {
- commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT;
- startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1);
- }
- else
- {
- commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1);
- }
-
- enet_list_clear (& fragments);
-
- for (fragmentNumber = 0,
- fragmentOffset = 0;
- fragmentOffset < packet -> dataLength;
- ++ fragmentNumber,
- fragmentOffset += fragmentLength)
- {
- if (packet -> dataLength - fragmentOffset < fragmentLength)
- fragmentLength = packet -> dataLength - fragmentOffset;
-
- fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
- if (fragment == NULL)
- {
- while (! enet_list_empty (& fragments))
- {
- fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
-
- enet_free (fragment);
- }
-
- return -1;
- }
-
- fragment -> fragmentOffset = fragmentOffset;
- fragment -> fragmentLength = fragmentLength;
- fragment -> packet = packet;
- fragment -> command.header.command = commandNumber;
- fragment -> command.header.channelID = channelID;
- fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber;
- fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength);
- fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount);
- fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber);
- fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength);
- fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset);
-
- enet_list_insert (enet_list_end (& fragments), fragment);
- }
-
- packet -> referenceCount += fragmentNumber;
-
- while (! enet_list_empty (& fragments))
- {
- fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments));
-
- enet_peer_setup_outgoing_command (peer, fragment);
- }
-
- return 0;
- }
-
- command.header.channelID = channelID;
-
- if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED)
- {
- command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
- command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
- }
- else
- if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF)
- {
- command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
- }
- else
- {
- command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE;
- command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength);
- }
-
- if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL)
- return -1;
-
- return 0;
-}
-
-/** Attempts to dequeue any incoming queued packet.
- @param peer peer to dequeue packets from
- @param channelID holds the channel ID of the channel the packet was received on success
- @returns a pointer to the packet, or NULL if there are no available incoming queued packets
-*/
-ENetPacket *
-enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID)
-{
- ENetIncomingCommand * incomingCommand;
- ENetPacket * packet;
-
- if (enet_list_empty (& peer -> dispatchedCommands))
- return NULL;
-
- incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands));
-
- if (channelID != NULL)
- * channelID = incomingCommand -> command.header.channelID;
-
- packet = incomingCommand -> packet;
-
- -- packet -> referenceCount;
-
- if (incomingCommand -> fragments != NULL)
- enet_free (incomingCommand -> fragments);
-
- enet_free (incomingCommand);
-
- peer -> totalWaitingData -= packet -> dataLength;
-
- return packet;
-}
-
-static void
-enet_peer_reset_outgoing_commands (ENetList * queue)
-{
- ENetOutgoingCommand * outgoingCommand;
-
- while (! enet_list_empty (queue))
- {
- outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue));
-
- if (outgoingCommand -> packet != NULL)
- {
- -- outgoingCommand -> packet -> referenceCount;
-
- if (outgoingCommand -> packet -> referenceCount == 0)
- enet_packet_destroy (outgoingCommand -> packet);
- }
-
- enet_free (outgoingCommand);
- }
-}
-
-static void
-enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand)
-{
- ENetListIterator currentCommand;
-
- for (currentCommand = startCommand; currentCommand != endCommand; )
- {
- ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- currentCommand = enet_list_next (currentCommand);
-
- enet_list_remove (& incomingCommand -> incomingCommandList);
-
- if (incomingCommand -> packet != NULL)
- {
- -- incomingCommand -> packet -> referenceCount;
-
- if (incomingCommand -> packet -> referenceCount == 0)
- enet_packet_destroy (incomingCommand -> packet);
- }
-
- if (incomingCommand -> fragments != NULL)
- enet_free (incomingCommand -> fragments);
-
- enet_free (incomingCommand);
- }
-}
-
-static void
-enet_peer_reset_incoming_commands (ENetList * queue)
-{
- enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue));
-}
-
-void
-enet_peer_reset_queues (ENetPeer * peer)
-{
- ENetChannel * channel;
-
- if (peer -> needsDispatch)
- {
- enet_list_remove (& peer -> dispatchList);
-
- peer -> needsDispatch = 0;
- }
-
- while (! enet_list_empty (& peer -> acknowledgements))
- enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));
-
- enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands);
- enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands);
- enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands);
- enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands);
- enet_peer_reset_incoming_commands (& peer -> dispatchedCommands);
-
- if (peer -> channels != NULL && peer -> channelCount > 0)
- {
- for (channel = peer -> channels;
- channel < & peer -> channels [peer -> channelCount];
- ++ channel)
- {
- enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);
- enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);
- }
-
- enet_free (peer -> channels);
- }
-
- peer -> channels = NULL;
- peer -> channelCount = 0;
-}
-
-void
-enet_peer_on_connect (ENetPeer * peer)
-{
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- {
- if (peer -> incomingBandwidth != 0)
- ++ peer -> host -> bandwidthLimitedPeers;
-
- ++ peer -> host -> connectedPeers;
- }
-}
-
-void
-enet_peer_on_disconnect (ENetPeer * peer)
-{
- if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
- {
- if (peer -> incomingBandwidth != 0)
- -- peer -> host -> bandwidthLimitedPeers;
-
- -- peer -> host -> connectedPeers;
- }
-}
-
-/** Forcefully disconnects a peer.
- @param peer peer to forcefully disconnect
- @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout
- on its connection to the local host.
-*/
-void
-enet_peer_reset (ENetPeer * peer)
-{
- enet_peer_on_disconnect (peer);
-
- peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID;
- peer -> connectID = 0;
-
- peer -> state = ENET_PEER_STATE_DISCONNECTED;
-
- peer -> incomingBandwidth = 0;
- peer -> outgoingBandwidth = 0;
- peer -> incomingBandwidthThrottleEpoch = 0;
- peer -> outgoingBandwidthThrottleEpoch = 0;
- peer -> incomingDataTotal = 0;
- peer -> outgoingDataTotal = 0;
- peer -> lastSendTime = 0;
- peer -> lastReceiveTime = 0;
- peer -> nextTimeout = 0;
- peer -> earliestTimeout = 0;
- peer -> packetLossEpoch = 0;
- peer -> packetsSent = 0;
- peer -> packetsLost = 0;
- peer -> packetLoss = 0;
- peer -> packetLossVariance = 0;
- peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;
- peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;
- peer -> packetThrottleCounter = 0;
- peer -> packetThrottleEpoch = 0;
- peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;
- peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;
- peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;
- peer -> pingInterval = ENET_PEER_PING_INTERVAL;
- peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT;
- peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM;
- peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM;
- peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
- peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
- peer -> lastRoundTripTimeVariance = 0;
- peer -> highestRoundTripTimeVariance = 0;
- peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;
- peer -> roundTripTimeVariance = 0;
- peer -> mtu = peer -> host -> mtu;
- peer -> reliableDataInTransit = 0;
- peer -> outgoingReliableSequenceNumber = 0;
- peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
- peer -> incomingUnsequencedGroup = 0;
- peer -> outgoingUnsequencedGroup = 0;
- peer -> eventData = 0;
- peer -> totalWaitingData = 0;
-
- memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
-
- enet_peer_reset_queues (peer);
-}
-
-/** Sends a ping request to a peer.
- @param peer destination for the ping request
- @remarks ping requests factor into the mean round trip time as designated by the
- roundTripTime field in the ENetPeer structure. ENet automatically pings all connected
- peers at regular intervals, however, this function may be called to ensure more
- frequent ping requests.
-*/
-void
-enet_peer_ping (ENetPeer * peer)
-{
- ENetProtocol command;
-
- if (peer -> state != ENET_PEER_STATE_CONNECTED)
- return;
-
- command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- command.header.channelID = 0xFF;
-
- enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
-}
-
-/** Sets the interval at which pings will be sent to a peer.
-
- Pings are used both to monitor the liveness of the connection and also to dynamically
- adjust the throttle during periods of low traffic so that the throttle has reasonable
- responsiveness during traffic spikes.
-
- @param peer the peer to adjust
- @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0
-*/
-void
-enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval)
-{
- peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL;
-}
-
-/** Sets the timeout parameters for a peer.
-
- The timeout parameter control how and when a peer will timeout from a failure to acknowledge
- reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable
- packet is not acknowledge within some multiple of the average RTT plus a variance tolerance,
- the timeout will be doubled until it reaches a set limit. If the timeout is thus at this
- limit and reliable packets have been sent but not acknowledged within a certain minimum time
- period, the peer will be disconnected. Alternatively, if reliable packets have been sent
- but not acknowledged for a certain maximum time period, the peer will be disconnected regardless
- of the current timeout limit value.
-
- @param peer the peer to adjust
- @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0
- @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0
- @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0
-*/
-
-void
-enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum)
-{
- peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT;
- peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM;
- peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM;
-}
-
-/** Force an immediate disconnection from a peer.
- @param peer peer to disconnect
- @param data data describing the disconnection
- @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not
- guaranteed to receive the disconnect notification, and is reset immediately upon
- return from this function.
-*/
-void
-enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data)
-{
- ENetProtocol command;
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECTED)
- return;
-
- if (peer -> state != ENET_PEER_STATE_ZOMBIE &&
- peer -> state != ENET_PEER_STATE_DISCONNECTING)
- {
- enet_peer_reset_queues (peer);
-
- command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
- command.header.channelID = 0xFF;
- command.disconnect.data = ENET_HOST_TO_NET_32 (data);
-
- enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
-
- enet_host_flush (peer -> host);
- }
-
- enet_peer_reset (peer);
-}
-
-/** Request a disconnection from a peer.
- @param peer peer to request a disconnection
- @param data data describing the disconnection
- @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
- once the disconnection is complete.
-*/
-void
-enet_peer_disconnect (ENetPeer * peer, enet_uint32 data)
-{
- ENetProtocol command;
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECTING ||
- peer -> state == ENET_PEER_STATE_DISCONNECTED ||
- peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT ||
- peer -> state == ENET_PEER_STATE_ZOMBIE)
- return;
-
- enet_peer_reset_queues (peer);
-
- command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT;
- command.header.channelID = 0xFF;
- command.disconnect.data = ENET_HOST_TO_NET_32 (data);
-
- if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
- command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- else
- command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED;
-
- enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
-
- if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
- {
- enet_peer_on_disconnect (peer);
-
- peer -> state = ENET_PEER_STATE_DISCONNECTING;
- }
- else
- {
- enet_host_flush (peer -> host);
- enet_peer_reset (peer);
- }
-}
-
-/** Request a disconnection from a peer, but only after all queued outgoing packets are sent.
- @param peer peer to request a disconnection
- @param data data describing the disconnection
- @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service()
- once the disconnection is complete.
-*/
-void
-enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data)
-{
- if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) &&
- ! (enet_list_empty (& peer -> outgoingReliableCommands) &&
- enet_list_empty (& peer -> outgoingUnreliableCommands) &&
- enet_list_empty (& peer -> sentReliableCommands)))
- {
- peer -> state = ENET_PEER_STATE_DISCONNECT_LATER;
- peer -> eventData = data;
- }
- else
- enet_peer_disconnect (peer, data);
-}
-
-ENetAcknowledgement *
-enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime)
-{
- ENetAcknowledgement * acknowledgement;
-
- if (command -> header.channelID < peer -> channelCount)
- {
- ENetChannel * channel = & peer -> channels [command -> header.channelID];
- enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
- currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
-
- if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
-
- if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS)
- return NULL;
- }
-
- acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement));
- if (acknowledgement == NULL)
- return NULL;
-
- peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge);
-
- acknowledgement -> sentTime = sentTime;
- acknowledgement -> command = * command;
-
- enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement);
-
- return acknowledgement;
-}
-
-void
-enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand)
-{
- ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID];
-
- peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength;
-
- if (outgoingCommand -> command.header.channelID == 0xFF)
- {
- ++ peer -> outgoingReliableSequenceNumber;
-
- outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber;
- outgoingCommand -> unreliableSequenceNumber = 0;
- }
- else
- if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
- {
- ++ channel -> outgoingReliableSequenceNumber;
- channel -> outgoingUnreliableSequenceNumber = 0;
-
- outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
- outgoingCommand -> unreliableSequenceNumber = 0;
- }
- else
- if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED)
- {
- ++ peer -> outgoingUnsequencedGroup;
-
- outgoingCommand -> reliableSequenceNumber = 0;
- outgoingCommand -> unreliableSequenceNumber = 0;
- }
- else
- {
- if (outgoingCommand -> fragmentOffset == 0)
- ++ channel -> outgoingUnreliableSequenceNumber;
-
- outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber;
- outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber;
- }
-
- outgoingCommand -> sendAttempts = 0;
- outgoingCommand -> sentTime = 0;
- outgoingCommand -> roundTripTimeout = 0;
- outgoingCommand -> roundTripTimeoutLimit = 0;
- outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber);
-
- switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK)
- {
- case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
- outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber);
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
- outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup);
- break;
-
- default:
- break;
- }
-
- if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
- enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand);
- else
- enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand);
-}
-
-ENetOutgoingCommand *
-enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length)
-{
- ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand));
- if (outgoingCommand == NULL)
- return NULL;
-
- outgoingCommand -> command = * command;
- outgoingCommand -> fragmentOffset = offset;
- outgoingCommand -> fragmentLength = length;
- outgoingCommand -> packet = packet;
- if (packet != NULL)
- ++ packet -> referenceCount;
-
- enet_peer_setup_outgoing_command (peer, outgoingCommand);
-
- return outgoingCommand;
-}
-
-void
-enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel)
-{
- ENetListIterator droppedCommand, startCommand, currentCommand;
-
- for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands);
- currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
- currentCommand = enet_list_next (currentCommand))
- {
- ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
- continue;
-
- if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
- {
- if (incomingCommand -> fragmentsRemaining <= 0)
- {
- channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber;
- continue;
- }
-
- if (startCommand != currentCommand)
- {
- enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
-
- if (! peer -> needsDispatch)
- {
- enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
-
- peer -> needsDispatch = 1;
- }
-
- droppedCommand = currentCommand;
- }
- else
- if (droppedCommand != currentCommand)
- droppedCommand = enet_list_previous (currentCommand);
- }
- else
- {
- enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE,
- currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
- if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
- break;
-
- droppedCommand = enet_list_next (currentCommand);
-
- if (startCommand != currentCommand)
- {
- enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
-
- if (! peer -> needsDispatch)
- {
- enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
-
- peer -> needsDispatch = 1;
- }
- }
- }
-
- startCommand = enet_list_next (currentCommand);
- }
-
- if (startCommand != currentCommand)
- {
- enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
-
- if (! peer -> needsDispatch)
- {
- enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
-
- peer -> needsDispatch = 1;
- }
-
- droppedCommand = currentCommand;
- }
-
- enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand);
-}
-
-void
-enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel)
-{
- ENetListIterator currentCommand;
-
- for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands);
- currentCommand != enet_list_end (& channel -> incomingReliableCommands);
- currentCommand = enet_list_next (currentCommand))
- {
- ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if (incomingCommand -> fragmentsRemaining > 0 ||
- incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1))
- break;
-
- channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber;
-
- if (incomingCommand -> fragmentCount > 0)
- channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1;
- }
-
- if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands))
- return;
-
- channel -> incomingUnreliableSequenceNumber = 0;
-
- enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand));
-
- if (! peer -> needsDispatch)
- {
- enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
-
- peer -> needsDispatch = 1;
- }
-
- if (! enet_list_empty (& channel -> incomingUnreliableCommands))
- enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
-}
-
-ENetIncomingCommand *
-enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount)
-{
- static ENetIncomingCommand dummyCommand;
-
- ENetChannel * channel = & peer -> channels [command -> header.channelID];
- enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0;
- enet_uint16 reliableWindow, currentWindow;
- ENetIncomingCommand * incomingCommand;
- ENetListIterator currentCommand;
- ENetPacket * packet = NULL;
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER)
- goto discardCommand;
-
- if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
- {
- reliableSequenceNumber = command -> header.reliableSequenceNumber;
- reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
-
- if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
-
- if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
- goto discardCommand;
- }
-
- switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
- {
- case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
- case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
- if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber)
- goto discardCommand;
-
- for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
- currentCommand != enet_list_end (& channel -> incomingReliableCommands);
- currentCommand = enet_list_previous (currentCommand))
- {
- incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- continue;
- }
- else
- if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
- break;
-
- goto discardCommand;
- }
- }
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
- case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
- unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber);
-
- if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber &&
- unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
- goto discardCommand;
-
- for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
- currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
- currentCommand = enet_list_previous (currentCommand))
- {
- incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED)
- continue;
-
- if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- continue;
- }
- else
- if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
- continue;
-
- if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber)
- {
- if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber)
- break;
-
- goto discardCommand;
- }
- }
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
- currentCommand = enet_list_end (& channel -> incomingUnreliableCommands);
- break;
-
- default:
- goto discardCommand;
- }
-
- if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData)
- goto notifyError;
-
- packet = enet_packet_create (data, dataLength, flags);
- if (packet == NULL)
- goto notifyError;
-
- incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand));
- if (incomingCommand == NULL)
- goto notifyError;
-
- incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber;
- incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF;
- incomingCommand -> command = * command;
- incomingCommand -> fragmentCount = fragmentCount;
- incomingCommand -> fragmentsRemaining = fragmentCount;
- incomingCommand -> packet = packet;
- incomingCommand -> fragments = NULL;
-
- if (fragmentCount > 0)
- {
- if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT)
- incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32));
- if (incomingCommand -> fragments == NULL)
- {
- enet_free (incomingCommand);
-
- goto notifyError;
- }
- memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32));
- }
-
- if (packet != NULL)
- {
- ++ packet -> referenceCount;
-
- peer -> totalWaitingData += packet -> dataLength;
- }
-
- enet_list_insert (enet_list_next (currentCommand), incomingCommand);
-
- switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK)
- {
- case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
- case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
- enet_peer_dispatch_incoming_reliable_commands (peer, channel);
- break;
-
- default:
- enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
- break;
- }
-
- return incomingCommand;
-
-discardCommand:
- if (fragmentCount > 0)
- goto notifyError;
-
- if (packet != NULL && packet -> referenceCount == 0)
- enet_packet_destroy (packet);
-
- return & dummyCommand;
-
-notifyError:
- if (packet != NULL && packet -> referenceCount == 0)
- enet_packet_destroy (packet);
-
- return NULL;
-}
-
-/** @} */
diff --git a/modules/enet/protocol.c b/modules/enet/protocol.c
deleted file mode 100644
index 4a2a4ed185..0000000000
--- a/modules/enet/protocol.c
+++ /dev/null
@@ -1,1914 +0,0 @@
-/**
- @file protocol.c
- @brief ENet protocol functions
-*/
-#include <stdio.h>
-#include <string.h>
-#define ENET_BUILDING_LIB 1
-#include "enet/utility.h"
-#include "enet/time.h"
-#include "enet/enet.h"
-
-
-static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] =
-{
- 0,
- sizeof (ENetProtocolAcknowledge),
- sizeof (ENetProtocolConnect),
- sizeof (ENetProtocolVerifyConnect),
- sizeof (ENetProtocolDisconnect),
- sizeof (ENetProtocolPing),
- sizeof (ENetProtocolSendReliable),
- sizeof (ENetProtocolSendUnreliable),
- sizeof (ENetProtocolSendFragment),
- sizeof (ENetProtocolSendUnsequenced),
- sizeof (ENetProtocolBandwidthLimit),
- sizeof (ENetProtocolThrottleConfigure),
- sizeof (ENetProtocolSendFragment)
-};
-
-size_t
-enet_protocol_command_size (enet_uint8 commandNumber)
-{
- return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK];
-}
-
-static void
-enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
-{
- if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER)
- enet_peer_on_connect (peer);
- else
- enet_peer_on_disconnect (peer);
-
- peer -> state = state;
-}
-
-static void
-enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state)
-{
- enet_protocol_change_state (host, peer, state);
-
- if (! peer -> needsDispatch)
- {
- enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
-
- peer -> needsDispatch = 1;
- }
-}
-
-static int
-enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event)
-{
- while (! enet_list_empty (& host -> dispatchQueue))
- {
- ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue));
-
- peer -> needsDispatch = 0;
-
- switch (peer -> state)
- {
- case ENET_PEER_STATE_CONNECTION_PENDING:
- case ENET_PEER_STATE_CONNECTION_SUCCEEDED:
- enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
-
- event -> type = ENET_EVENT_TYPE_CONNECT;
- event -> peer = peer;
- event -> data = peer -> eventData;
-
- return 1;
-
- case ENET_PEER_STATE_ZOMBIE:
- host -> recalculateBandwidthLimits = 1;
-
- event -> type = ENET_EVENT_TYPE_DISCONNECT;
- event -> peer = peer;
- event -> data = peer -> eventData;
-
- enet_peer_reset (peer);
-
- return 1;
-
- case ENET_PEER_STATE_CONNECTED:
- if (enet_list_empty (& peer -> dispatchedCommands))
- continue;
-
- event -> packet = enet_peer_receive (peer, & event -> channelID);
- if (event -> packet == NULL)
- continue;
-
- event -> type = ENET_EVENT_TYPE_RECEIVE;
- event -> peer = peer;
-
- if (! enet_list_empty (& peer -> dispatchedCommands))
- {
- peer -> needsDispatch = 1;
-
- enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList);
- }
-
- return 1;
-
- default:
- break;
- }
- }
-
- return 0;
-}
-
-static void
-enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
-{
- host -> recalculateBandwidthLimits = 1;
-
- if (event != NULL)
- {
- enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED);
-
- event -> type = ENET_EVENT_TYPE_CONNECT;
- event -> peer = peer;
- event -> data = peer -> eventData;
- }
- else
- enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING);
-}
-
-static void
-enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event)
-{
- if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING)
- host -> recalculateBandwidthLimits = 1;
-
- if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED)
- enet_peer_reset (peer);
- else
- if (event != NULL)
- {
- event -> type = ENET_EVENT_TYPE_DISCONNECT;
- event -> peer = peer;
- event -> data = 0;
-
- enet_peer_reset (peer);
- }
- else
- {
- peer -> eventData = 0;
-
- enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
- }
-}
-
-static void
-enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer)
-{
- ENetOutgoingCommand * outgoingCommand;
-
- while (! enet_list_empty (& peer -> sentUnreliableCommands))
- {
- outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands);
-
- enet_list_remove (& outgoingCommand -> outgoingCommandList);
-
- if (outgoingCommand -> packet != NULL)
- {
- -- outgoingCommand -> packet -> referenceCount;
-
- if (outgoingCommand -> packet -> referenceCount == 0)
- {
- outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
-
- enet_packet_destroy (outgoingCommand -> packet);
- }
- }
-
- enet_free (outgoingCommand);
- }
-}
-
-static ENetProtocolCommand
-enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID)
-{
- ENetOutgoingCommand * outgoingCommand = NULL;
- ENetListIterator currentCommand;
- ENetProtocolCommand commandNumber;
- int wasSent = 1;
-
- for (currentCommand = enet_list_begin (& peer -> sentReliableCommands);
- currentCommand != enet_list_end (& peer -> sentReliableCommands);
- currentCommand = enet_list_next (currentCommand))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
- outgoingCommand -> command.header.channelID == channelID)
- break;
- }
-
- if (currentCommand == enet_list_end (& peer -> sentReliableCommands))
- {
- for (currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
- currentCommand != enet_list_end (& peer -> outgoingReliableCommands);
- currentCommand = enet_list_next (currentCommand))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE;
-
- if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber &&
- outgoingCommand -> command.header.channelID == channelID)
- break;
- }
-
- if (currentCommand == enet_list_end (& peer -> outgoingReliableCommands))
- return ENET_PROTOCOL_COMMAND_NONE;
-
- wasSent = 0;
- }
-
- if (outgoingCommand == NULL)
- return ENET_PROTOCOL_COMMAND_NONE;
-
- if (channelID < peer -> channelCount)
- {
- ENetChannel * channel = & peer -> channels [channelID];
- enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- if (channel -> reliableWindows [reliableWindow] > 0)
- {
- -- channel -> reliableWindows [reliableWindow];
- if (! channel -> reliableWindows [reliableWindow])
- channel -> usedReliableWindows &= ~ (1 << reliableWindow);
- }
- }
-
- commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK);
-
- enet_list_remove (& outgoingCommand -> outgoingCommandList);
-
- if (outgoingCommand -> packet != NULL)
- {
- if (wasSent)
- peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
-
- -- outgoingCommand -> packet -> referenceCount;
-
- if (outgoingCommand -> packet -> referenceCount == 0)
- {
- outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT;
-
- enet_packet_destroy (outgoingCommand -> packet);
- }
- }
-
- enet_free (outgoingCommand);
-
- if (enet_list_empty (& peer -> sentReliableCommands))
- return commandNumber;
-
- outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands);
-
- peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
-
- return commandNumber;
-}
-
-static ENetPeer *
-enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command)
-{
- enet_uint8 incomingSessionID, outgoingSessionID;
- enet_uint32 mtu, windowSize;
- ENetChannel * channel;
- size_t channelCount, duplicatePeers = 0;
- ENetPeer * currentPeer, * peer = NULL;
- ENetProtocol verifyCommand;
-
- channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount);
-
- if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT ||
- channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
- return NULL;
-
- for (currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
- {
- if (peer == NULL)
- peer = currentPeer;
- }
- else
- if (currentPeer -> state != ENET_PEER_STATE_CONNECTING &&
- currentPeer -> address.host == host -> receivedAddress.host)
- {
- if (currentPeer -> address.port == host -> receivedAddress.port &&
- currentPeer -> connectID == command -> connect.connectID)
- return NULL;
-
- ++ duplicatePeers;
- }
- }
-
- if (peer == NULL || duplicatePeers >= host -> duplicatePeers)
- return NULL;
-
- if (channelCount > host -> channelLimit)
- channelCount = host -> channelLimit;
- peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
- if (peer -> channels == NULL)
- return NULL;
- peer -> channelCount = channelCount;
- peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT;
- peer -> connectID = command -> connect.connectID;
- peer -> address = host -> receivedAddress;
- peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID);
- peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth);
- peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth);
- peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval);
- peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration);
- peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration);
- peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data);
-
- incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID;
- incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
- if (incomingSessionID == peer -> outgoingSessionID)
- incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
- peer -> outgoingSessionID = incomingSessionID;
-
- outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID;
- outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
- if (outgoingSessionID == peer -> incomingSessionID)
- outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT);
- peer -> incomingSessionID = outgoingSessionID;
-
- for (channel = peer -> channels;
- channel < & peer -> channels [channelCount];
- ++ channel)
- {
- channel -> outgoingReliableSequenceNumber = 0;
- channel -> outgoingUnreliableSequenceNumber = 0;
- channel -> incomingReliableSequenceNumber = 0;
- channel -> incomingUnreliableSequenceNumber = 0;
-
- enet_list_clear (& channel -> incomingReliableCommands);
- enet_list_clear (& channel -> incomingUnreliableCommands);
-
- channel -> usedReliableWindows = 0;
- memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
- }
-
- mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu);
-
- if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
- mtu = ENET_PROTOCOL_MINIMUM_MTU;
- else
- if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
- mtu = ENET_PROTOCOL_MAXIMUM_MTU;
-
- peer -> mtu = mtu;
-
- if (host -> outgoingBandwidth == 0 &&
- peer -> incomingBandwidth == 0)
- peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
- else
- if (host -> outgoingBandwidth == 0 ||
- peer -> incomingBandwidth == 0)
- peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) /
- ENET_PEER_WINDOW_SIZE_SCALE) *
- ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) /
- ENET_PEER_WINDOW_SIZE_SCALE) *
- ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
-
- if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
- peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
- peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
-
- if (host -> incomingBandwidth == 0)
- windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
- else
- windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) *
- ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
-
- if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize))
- windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize);
-
- if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
- windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
- windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
-
- verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
- verifyCommand.header.channelID = 0xFF;
- verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID);
- verifyCommand.verifyConnect.incomingSessionID = incomingSessionID;
- verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID;
- verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu);
- verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize);
- verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
- verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
- verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
- verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval);
- verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration);
- verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration);
- verifyCommand.verifyConnect.connectID = peer -> connectID;
-
- enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0);
-
- return peer;
-}
-
-static int
-enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
-{
- size_t dataLength;
-
- if (command -> header.channelID >= peer -> channelCount ||
- (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
- return -1;
-
- dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength);
- * currentData += dataLength;
- if (dataLength > host -> maximumPacketSize ||
- * currentData < host -> receivedData ||
- * currentData > & host -> receivedData [host -> receivedDataLength])
- return -1;
-
- if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL)
- return -1;
-
- return 0;
-}
-
-static int
-enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
-{
- enet_uint32 unsequencedGroup, index;
- size_t dataLength;
-
- if (command -> header.channelID >= peer -> channelCount ||
- (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
- return -1;
-
- dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength);
- * currentData += dataLength;
- if (dataLength > host -> maximumPacketSize ||
- * currentData < host -> receivedData ||
- * currentData > & host -> receivedData [host -> receivedDataLength])
- return -1;
-
- unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup);
- index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE;
-
- if (unsequencedGroup < peer -> incomingUnsequencedGroup)
- unsequencedGroup += 0x10000;
-
- if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE)
- return 0;
-
- unsequencedGroup &= 0xFFFF;
-
- if (unsequencedGroup - index != peer -> incomingUnsequencedGroup)
- {
- peer -> incomingUnsequencedGroup = unsequencedGroup - index;
-
- memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow));
- }
- else
- if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32)))
- return 0;
-
- if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL)
- return -1;
-
- peer -> unsequencedWindow [index / 32] |= 1 << (index % 32);
-
- return 0;
-}
-
-static int
-enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
-{
- size_t dataLength;
-
- if (command -> header.channelID >= peer -> channelCount ||
- (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
- return -1;
-
- dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength);
- * currentData += dataLength;
- if (dataLength > host -> maximumPacketSize ||
- * currentData < host -> receivedData ||
- * currentData > & host -> receivedData [host -> receivedDataLength])
- return -1;
-
- if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL)
- return -1;
-
- return 0;
-}
-
-static int
-enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
-{
- enet_uint32 fragmentNumber,
- fragmentCount,
- fragmentOffset,
- fragmentLength,
- startSequenceNumber,
- totalLength;
- ENetChannel * channel;
- enet_uint16 startWindow, currentWindow;
- ENetListIterator currentCommand;
- ENetIncomingCommand * startCommand = NULL;
-
- if (command -> header.channelID >= peer -> channelCount ||
- (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
- return -1;
-
- fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
- * currentData += fragmentLength;
- if (fragmentLength > host -> maximumPacketSize ||
- * currentData < host -> receivedData ||
- * currentData > & host -> receivedData [host -> receivedDataLength])
- return -1;
-
- channel = & peer -> channels [command -> header.channelID];
- startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
- startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
-
- if (startSequenceNumber < channel -> incomingReliableSequenceNumber)
- startWindow += ENET_PEER_RELIABLE_WINDOWS;
-
- if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
- return 0;
-
- fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
- fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
- fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
- totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
-
- if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
- fragmentNumber >= fragmentCount ||
- totalLength > host -> maximumPacketSize ||
- fragmentOffset >= totalLength ||
- fragmentLength > totalLength - fragmentOffset)
- return -1;
-
- for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands));
- currentCommand != enet_list_end (& channel -> incomingReliableCommands);
- currentCommand = enet_list_previous (currentCommand))
- {
- ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if (startSequenceNumber >= channel -> incomingReliableSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- continue;
- }
- else
- if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < startSequenceNumber)
- break;
-
- if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT ||
- totalLength != incomingCommand -> packet -> dataLength ||
- fragmentCount != incomingCommand -> fragmentCount)
- return -1;
-
- startCommand = incomingCommand;
- break;
- }
- }
-
- if (startCommand == NULL)
- {
- ENetProtocol hostCommand = * command;
-
- hostCommand.header.reliableSequenceNumber = startSequenceNumber;
-
- startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount);
- if (startCommand == NULL)
- return -1;
- }
-
- if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
- {
- -- startCommand -> fragmentsRemaining;
-
- startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
-
- if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
- fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
-
- memcpy (startCommand -> packet -> data + fragmentOffset,
- (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
- fragmentLength);
-
- if (startCommand -> fragmentsRemaining <= 0)
- enet_peer_dispatch_incoming_reliable_commands (peer, channel);
- }
-
- return 0;
-}
-
-static int
-enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData)
-{
- enet_uint32 fragmentNumber,
- fragmentCount,
- fragmentOffset,
- fragmentLength,
- reliableSequenceNumber,
- startSequenceNumber,
- totalLength;
- enet_uint16 reliableWindow, currentWindow;
- ENetChannel * channel;
- ENetListIterator currentCommand;
- ENetIncomingCommand * startCommand = NULL;
-
- if (command -> header.channelID >= peer -> channelCount ||
- (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER))
- return -1;
-
- fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength);
- * currentData += fragmentLength;
- if (fragmentLength > host -> maximumPacketSize ||
- * currentData < host -> receivedData ||
- * currentData > & host -> receivedData [host -> receivedDataLength])
- return -1;
-
- channel = & peer -> channels [command -> header.channelID];
- reliableSequenceNumber = command -> header.reliableSequenceNumber;
- startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber);
-
- reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
-
- if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- reliableWindow += ENET_PEER_RELIABLE_WINDOWS;
-
- if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1)
- return 0;
-
- if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber &&
- startSequenceNumber <= channel -> incomingUnreliableSequenceNumber)
- return 0;
-
- fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber);
- fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount);
- fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset);
- totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength);
-
- if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT ||
- fragmentNumber >= fragmentCount ||
- totalLength > host -> maximumPacketSize ||
- fragmentOffset >= totalLength ||
- fragmentLength > totalLength - fragmentOffset)
- return -1;
-
- for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands));
- currentCommand != enet_list_end (& channel -> incomingUnreliableCommands);
- currentCommand = enet_list_previous (currentCommand))
- {
- ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand;
-
- if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- {
- if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber)
- continue;
- }
- else
- if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber)
- break;
-
- if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber)
- continue;
-
- if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber)
- {
- if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber)
- break;
-
- if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT ||
- totalLength != incomingCommand -> packet -> dataLength ||
- fragmentCount != incomingCommand -> fragmentCount)
- return -1;
-
- startCommand = incomingCommand;
- break;
- }
- }
-
- if (startCommand == NULL)
- {
- startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount);
- if (startCommand == NULL)
- return -1;
- }
-
- if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0)
- {
- -- startCommand -> fragmentsRemaining;
-
- startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32));
-
- if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength)
- fragmentLength = startCommand -> packet -> dataLength - fragmentOffset;
-
- memcpy (startCommand -> packet -> data + fragmentOffset,
- (enet_uint8 *) command + sizeof (ENetProtocolSendFragment),
- fragmentLength);
-
- if (startCommand -> fragmentsRemaining <= 0)
- enet_peer_dispatch_incoming_unreliable_commands (peer, channel);
- }
-
- return 0;
-}
-
-static int
-enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
-{
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- return -1;
-
- return 0;
-}
-
-static int
-enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
-{
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- return -1;
-
- if (peer -> incomingBandwidth != 0)
- -- host -> bandwidthLimitedPeers;
-
- peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth);
- peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth);
-
- if (peer -> incomingBandwidth != 0)
- ++ host -> bandwidthLimitedPeers;
-
- if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0)
- peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
- else
- if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0)
- peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) /
- ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) /
- ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
-
- if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
- peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
- else
- if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
- peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
-
- return 0;
-}
-
-static int
-enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
-{
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- return -1;
-
- peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval);
- peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration);
- peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration);
-
- return 0;
-}
-
-static int
-enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command)
-{
- if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT)
- return 0;
-
- enet_peer_reset_queues (peer);
-
- if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING)
- enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
- else
- if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
- {
- if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1;
-
- enet_peer_reset (peer);
- }
- else
- if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE)
- enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT);
- else
- enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
-
- if (peer -> state != ENET_PEER_STATE_DISCONNECTED)
- peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data);
-
- return 0;
-}
-
-static int
-enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
-{
- enet_uint32 roundTripTime,
- receivedSentTime,
- receivedReliableSequenceNumber;
- ENetProtocolCommand commandNumber;
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE)
- return 0;
-
- receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime);
- receivedSentTime |= host -> serviceTime & 0xFFFF0000;
- if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000))
- receivedSentTime -= 0x10000;
-
- if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime))
- return 0;
-
- peer -> lastReceiveTime = host -> serviceTime;
- peer -> earliestTimeout = 0;
-
- roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime);
-
- enet_peer_throttle (peer, roundTripTime);
-
- peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4;
-
- if (roundTripTime >= peer -> roundTripTime)
- {
- peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8;
- peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4;
- }
- else
- {
- peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8;
- peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4;
- }
-
- if (peer -> roundTripTime < peer -> lowestRoundTripTime)
- peer -> lowestRoundTripTime = peer -> roundTripTime;
-
- if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance)
- peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
-
- if (peer -> packetThrottleEpoch == 0 ||
- ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval)
- {
- peer -> lastRoundTripTime = peer -> lowestRoundTripTime;
- peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance;
- peer -> lowestRoundTripTime = peer -> roundTripTime;
- peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance;
- peer -> packetThrottleEpoch = host -> serviceTime;
- }
-
- receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber);
-
- commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID);
-
- switch (peer -> state)
- {
- case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
- if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT)
- return -1;
-
- enet_protocol_notify_connect (host, peer, event);
- break;
-
- case ENET_PEER_STATE_DISCONNECTING:
- if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT)
- return -1;
-
- enet_protocol_notify_disconnect (host, peer, event);
- break;
-
- case ENET_PEER_STATE_DISCONNECT_LATER:
- if (enet_list_empty (& peer -> outgoingReliableCommands) &&
- enet_list_empty (& peer -> outgoingUnreliableCommands) &&
- enet_list_empty (& peer -> sentReliableCommands))
- enet_peer_disconnect (peer, peer -> eventData);
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-static int
-enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command)
-{
- enet_uint32 mtu, windowSize;
- size_t channelCount;
-
- if (peer -> state != ENET_PEER_STATE_CONNECTING)
- return 0;
-
- channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount);
-
- if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT ||
- ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval ||
- ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration ||
- ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration ||
- command -> verifyConnect.connectID != peer -> connectID)
- {
- peer -> eventData = 0;
-
- enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
-
- return -1;
- }
-
- enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF);
-
- if (channelCount < peer -> channelCount)
- peer -> channelCount = channelCount;
-
- peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID);
- peer -> incomingSessionID = command -> verifyConnect.incomingSessionID;
- peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID;
-
- mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu);
-
- if (mtu < ENET_PROTOCOL_MINIMUM_MTU)
- mtu = ENET_PROTOCOL_MINIMUM_MTU;
- else
- if (mtu > ENET_PROTOCOL_MAXIMUM_MTU)
- mtu = ENET_PROTOCOL_MAXIMUM_MTU;
-
- if (mtu < peer -> mtu)
- peer -> mtu = mtu;
-
- windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize);
-
- if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
- windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
-
- if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
- windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
-
- if (windowSize < peer -> windowSize)
- peer -> windowSize = windowSize;
-
- peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth);
- peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth);
-
- enet_protocol_notify_connect (host, peer, event);
- return 0;
-}
-
-static int
-enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event)
-{
- ENetProtocolHeader * header;
- ENetProtocol * command;
- ENetPeer * peer;
- enet_uint8 * currentData;
- size_t headerSize;
- enet_uint16 peerID, flags;
- enet_uint8 sessionID;
-
- if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime)
- return 0;
-
- header = (ENetProtocolHeader *) host -> receivedData;
-
- peerID = ENET_NET_TO_HOST_16 (header -> peerID);
- sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT;
- flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK;
- peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK);
-
- headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime);
- if (host -> checksum != NULL)
- headerSize += sizeof (enet_uint32);
-
- if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID)
- peer = NULL;
- else
- if (peerID >= host -> peerCount)
- return 0;
- else
- {
- peer = & host -> peers [peerID];
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECTED ||
- peer -> state == ENET_PEER_STATE_ZOMBIE ||
- ((host -> receivedAddress.host != peer -> address.host ||
- host -> receivedAddress.port != peer -> address.port) &&
- peer -> address.host != ENET_HOST_BROADCAST) ||
- (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID &&
- sessionID != peer -> incomingSessionID))
- return 0;
- }
-
- if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED)
- {
- size_t originalSize;
- if (host -> compressor.context == NULL || host -> compressor.decompress == NULL)
- return 0;
-
- originalSize = host -> compressor.decompress (host -> compressor.context,
- host -> receivedData + headerSize,
- host -> receivedDataLength - headerSize,
- host -> packetData [1] + headerSize,
- sizeof (host -> packetData [1]) - headerSize);
- if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize)
- return 0;
-
- memcpy (host -> packetData [1], header, headerSize);
- host -> receivedData = host -> packetData [1];
- host -> receivedDataLength = headerSize + originalSize;
- }
-
- if (host -> checksum != NULL)
- {
- enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)],
- desiredChecksum = * checksum;
- ENetBuffer buffer;
-
- * checksum = peer != NULL ? peer -> connectID : 0;
-
- buffer.data = host -> receivedData;
- buffer.dataLength = host -> receivedDataLength;
-
- if (host -> checksum (& buffer, 1) != desiredChecksum)
- return 0;
- }
-
- if (peer != NULL)
- {
- peer -> address.host = host -> receivedAddress.host;
- peer -> address.port = host -> receivedAddress.port;
- peer -> incomingDataTotal += host -> receivedDataLength;
- }
-
- currentData = host -> receivedData + headerSize;
-
- while (currentData < & host -> receivedData [host -> receivedDataLength])
- {
- enet_uint8 commandNumber;
- size_t commandSize;
-
- command = (ENetProtocol *) currentData;
-
- if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength])
- break;
-
- commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK;
- if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT)
- break;
-
- commandSize = commandSizes [commandNumber];
- if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength])
- break;
-
- currentData += commandSize;
-
- if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT)
- break;
-
- command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber);
-
- switch (commandNumber)
- {
- case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE:
- if (enet_protocol_handle_acknowledge (host, event, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_CONNECT:
- if (peer != NULL)
- goto commandError;
- peer = enet_protocol_handle_connect (host, header, command);
- if (peer == NULL)
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT:
- if (enet_protocol_handle_verify_connect (host, event, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_DISCONNECT:
- if (enet_protocol_handle_disconnect (host, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_PING:
- if (enet_protocol_handle_ping (host, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_RELIABLE:
- if (enet_protocol_handle_send_reliable (host, peer, command, & currentData))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE:
- if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED:
- if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT:
- if (enet_protocol_handle_send_fragment (host, peer, command, & currentData))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT:
- if (enet_protocol_handle_bandwidth_limit (host, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE:
- if (enet_protocol_handle_throttle_configure (host, peer, command))
- goto commandError;
- break;
-
- case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT:
- if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData))
- goto commandError;
- break;
-
- default:
- goto commandError;
- }
-
- if (peer != NULL &&
- (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0)
- {
- enet_uint16 sentTime;
-
- if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME))
- break;
-
- sentTime = ENET_NET_TO_HOST_16 (header -> sentTime);
-
- switch (peer -> state)
- {
- case ENET_PEER_STATE_DISCONNECTING:
- case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT:
- case ENET_PEER_STATE_DISCONNECTED:
- case ENET_PEER_STATE_ZOMBIE:
- break;
-
- case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT:
- if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
- enet_peer_queue_acknowledgement (peer, command, sentTime);
- break;
-
- default:
- enet_peer_queue_acknowledgement (peer, command, sentTime);
- break;
- }
- }
- }
-
-commandError:
- if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
- return 1;
-
- return 0;
-}
-
-static int
-enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event)
-{
- int packets;
-
- for (packets = 0; packets < 256; ++ packets)
- {
- int receivedLength;
- ENetBuffer buffer;
-
- buffer.data = host -> packetData [0];
- buffer.dataLength = sizeof (host -> packetData [0]);
-
- receivedLength = enet_socket_receive (host -> socket,
- & host -> receivedAddress,
- & buffer,
- 1);
-
- if (receivedLength < 0)
- return -1;
-
- if (receivedLength == 0)
- return 0;
-
- host -> receivedData = host -> packetData [0];
- host -> receivedDataLength = receivedLength;
-
- host -> totalReceivedData += receivedLength;
- host -> totalReceivedPackets ++;
-
- if (host -> intercept != NULL)
- {
- switch (host -> intercept (host, event))
- {
- case 1:
- if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
- return 1;
-
- continue;
-
- case -1:
- return -1;
-
- default:
- break;
- }
- }
-
- switch (enet_protocol_handle_incoming_commands (host, event))
- {
- case 1:
- return 1;
-
- case -1:
- return -1;
-
- default:
- break;
- }
- }
-
- return -1;
-}
-
-static void
-enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer)
-{
- ENetProtocol * command = & host -> commands [host -> commandCount];
- ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
- ENetAcknowledgement * acknowledgement;
- ENetListIterator currentAcknowledgement;
- enet_uint16 reliableSequenceNumber;
-
- currentAcknowledgement = enet_list_begin (& peer -> acknowledgements);
-
- while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements))
- {
- if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
- buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
- peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge))
- {
- host -> continueSending = 1;
-
- break;
- }
-
- acknowledgement = (ENetAcknowledgement *) currentAcknowledgement;
-
- currentAcknowledgement = enet_list_next (currentAcknowledgement);
-
- buffer -> data = command;
- buffer -> dataLength = sizeof (ENetProtocolAcknowledge);
-
- host -> packetSize += buffer -> dataLength;
-
- reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber);
-
- command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE;
- command -> header.channelID = acknowledgement -> command.header.channelID;
- command -> header.reliableSequenceNumber = reliableSequenceNumber;
- command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber;
- command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime);
-
- if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT)
- enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE);
-
- enet_list_remove (& acknowledgement -> acknowledgementList);
- enet_free (acknowledgement);
-
- ++ command;
- ++ buffer;
- }
-
- host -> commandCount = command - host -> commands;
- host -> bufferCount = buffer - host -> buffers;
-}
-
-static void
-enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
-{
- ENetProtocol * command = & host -> commands [host -> commandCount];
- ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
- ENetOutgoingCommand * outgoingCommand;
- ENetListIterator currentCommand;
-
- currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands);
-
- while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands))
- {
- size_t commandSize;
-
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
- commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK];
-
- if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
- buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
- peer -> mtu - host -> packetSize < commandSize ||
- (outgoingCommand -> packet != NULL &&
- peer -> mtu - host -> packetSize < commandSize + outgoingCommand -> fragmentLength))
- {
- host -> continueSending = 1;
-
- break;
- }
-
- currentCommand = enet_list_next (currentCommand);
-
- if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0)
- {
- peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER;
- peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE;
-
- if (peer -> packetThrottleCounter > peer -> packetThrottle)
- {
- enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber,
- unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber;
- for (;;)
- {
- -- outgoingCommand -> packet -> referenceCount;
-
- if (outgoingCommand -> packet -> referenceCount == 0)
- enet_packet_destroy (outgoingCommand -> packet);
-
- enet_list_remove (& outgoingCommand -> outgoingCommandList);
- enet_free (outgoingCommand);
-
- if (currentCommand == enet_list_end (& peer -> outgoingUnreliableCommands))
- break;
-
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
- if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber ||
- outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber)
- break;
-
- currentCommand = enet_list_next (currentCommand);
- }
-
- continue;
- }
- }
-
- buffer -> data = command;
- buffer -> dataLength = commandSize;
-
- host -> packetSize += buffer -> dataLength;
-
- * command = outgoingCommand -> command;
-
- enet_list_remove (& outgoingCommand -> outgoingCommandList);
-
- if (outgoingCommand -> packet != NULL)
- {
- ++ buffer;
-
- buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
- buffer -> dataLength = outgoingCommand -> fragmentLength;
-
- host -> packetSize += buffer -> dataLength;
-
- enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand);
- }
- else
- enet_free (outgoingCommand);
-
- ++ command;
- ++ buffer;
- }
-
- host -> commandCount = command - host -> commands;
- host -> bufferCount = buffer - host -> buffers;
-
- if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER &&
- enet_list_empty (& peer -> outgoingReliableCommands) &&
- enet_list_empty (& peer -> outgoingUnreliableCommands) &&
- enet_list_empty (& peer -> sentReliableCommands))
- enet_peer_disconnect (peer, peer -> eventData);
-}
-
-static int
-enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event)
-{
- ENetOutgoingCommand * outgoingCommand;
- ENetListIterator currentCommand, insertPosition;
-
- currentCommand = enet_list_begin (& peer -> sentReliableCommands);
- insertPosition = enet_list_begin (& peer -> outgoingReliableCommands);
-
- while (currentCommand != enet_list_end (& peer -> sentReliableCommands))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- currentCommand = enet_list_next (currentCommand);
-
- if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout)
- continue;
-
- if (peer -> earliestTimeout == 0 ||
- ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout))
- peer -> earliestTimeout = outgoingCommand -> sentTime;
-
- if (peer -> earliestTimeout != 0 &&
- (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum ||
- (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit &&
- ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum)))
- {
- enet_protocol_notify_disconnect (host, peer, event);
-
- return 1;
- }
-
- if (outgoingCommand -> packet != NULL)
- peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength;
-
- ++ peer -> packetsLost;
-
- outgoingCommand -> roundTripTimeout *= 2;
-
- enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList));
-
- if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) &&
- ! enet_list_empty (& peer -> sentReliableCommands))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout;
- }
- }
-
- return 0;
-}
-
-static int
-enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer)
-{
- ENetProtocol * command = & host -> commands [host -> commandCount];
- ENetBuffer * buffer = & host -> buffers [host -> bufferCount];
- ENetOutgoingCommand * outgoingCommand;
- ENetListIterator currentCommand;
- ENetChannel *channel;
- enet_uint16 reliableWindow;
- size_t commandSize;
- int windowExceeded = 0, windowWrap = 0, canPing = 1;
-
- currentCommand = enet_list_begin (& peer -> outgoingReliableCommands);
-
- while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands))
- {
- outgoingCommand = (ENetOutgoingCommand *) currentCommand;
-
- channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL;
- reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE;
- if (channel != NULL)
- {
- if (! windowWrap &&
- outgoingCommand -> sendAttempts < 1 &&
- ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) &&
- (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE ||
- channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) |
- (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))))
- windowWrap = 1;
- if (windowWrap)
- {
- currentCommand = enet_list_next (currentCommand);
-
- continue;
- }
- }
-
- if (outgoingCommand -> packet != NULL)
- {
- if (! windowExceeded)
- {
- enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE;
-
- if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu))
- windowExceeded = 1;
- }
- if (windowExceeded)
- {
- currentCommand = enet_list_next (currentCommand);
-
- continue;
- }
- }
-
- canPing = 0;
-
- commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK];
- if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] ||
- buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] ||
- peer -> mtu - host -> packetSize < commandSize ||
- (outgoingCommand -> packet != NULL &&
- (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength)))
- {
- host -> continueSending = 1;
-
- break;
- }
-
- currentCommand = enet_list_next (currentCommand);
-
- if (channel != NULL && outgoingCommand -> sendAttempts < 1)
- {
- channel -> usedReliableWindows |= 1 << reliableWindow;
- ++ channel -> reliableWindows [reliableWindow];
- }
-
- ++ outgoingCommand -> sendAttempts;
-
- if (outgoingCommand -> roundTripTimeout == 0)
- {
- outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance;
- outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout;
- }
-
- if (enet_list_empty (& peer -> sentReliableCommands))
- peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout;
-
- enet_list_insert (enet_list_end (& peer -> sentReliableCommands),
- enet_list_remove (& outgoingCommand -> outgoingCommandList));
-
- outgoingCommand -> sentTime = host -> serviceTime;
-
- buffer -> data = command;
- buffer -> dataLength = commandSize;
-
- host -> packetSize += buffer -> dataLength;
- host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME;
-
- * command = outgoingCommand -> command;
-
- if (outgoingCommand -> packet != NULL)
- {
- ++ buffer;
-
- buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset;
- buffer -> dataLength = outgoingCommand -> fragmentLength;
-
- host -> packetSize += outgoingCommand -> fragmentLength;
-
- peer -> reliableDataInTransit += outgoingCommand -> fragmentLength;
- }
-
- ++ peer -> packetsSent;
-
- ++ command;
- ++ buffer;
- }
-
- host -> commandCount = command - host -> commands;
- host -> bufferCount = buffer - host -> buffers;
-
- return canPing;
-}
-
-static int
-enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts)
-{
- enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)];
- ENetProtocolHeader * header = (ENetProtocolHeader *) headerData;
- ENetPeer * currentPeer;
- int sentLength;
- size_t shouldCompress = 0;
-
- host -> continueSending = 1;
-
- while (host -> continueSending)
- for (host -> continueSending = 0,
- currentPeer = host -> peers;
- currentPeer < & host -> peers [host -> peerCount];
- ++ currentPeer)
- {
- if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED ||
- currentPeer -> state == ENET_PEER_STATE_ZOMBIE)
- continue;
-
- host -> headerFlags = 0;
- host -> commandCount = 0;
- host -> bufferCount = 1;
- host -> packetSize = sizeof (ENetProtocolHeader);
-
- if (! enet_list_empty (& currentPeer -> acknowledgements))
- enet_protocol_send_acknowledgements (host, currentPeer);
-
- if (checkForTimeouts != 0 &&
- ! enet_list_empty (& currentPeer -> sentReliableCommands) &&
- ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) &&
- enet_protocol_check_timeouts (host, currentPeer, event) == 1)
- {
- if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE)
- return 1;
- else
- continue;
- }
-
- if ((enet_list_empty (& currentPeer -> outgoingReliableCommands) ||
- enet_protocol_send_reliable_outgoing_commands (host, currentPeer)) &&
- enet_list_empty (& currentPeer -> sentReliableCommands) &&
- ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval &&
- currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing))
- {
- enet_peer_ping (currentPeer);
- enet_protocol_send_reliable_outgoing_commands (host, currentPeer);
- }
-
- if (! enet_list_empty (& currentPeer -> outgoingUnreliableCommands))
- enet_protocol_send_unreliable_outgoing_commands (host, currentPeer);
-
- if (host -> commandCount == 0)
- continue;
-
- if (currentPeer -> packetLossEpoch == 0)
- currentPeer -> packetLossEpoch = host -> serviceTime;
- else
- if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL &&
- currentPeer -> packetsSent > 0)
- {
- enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent;
-
-#ifdef ENET_DEBUG
- printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0);
-#endif
-
- currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4;
-
- if (packetLoss >= currentPeer -> packetLoss)
- {
- currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8;
- currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4;
- }
- else
- {
- currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8;
- currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4;
- }
-
- currentPeer -> packetLossEpoch = host -> serviceTime;
- currentPeer -> packetsSent = 0;
- currentPeer -> packetsLost = 0;
- }
-
- host -> buffers -> data = headerData;
- if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)
- {
- header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF);
-
- host -> buffers -> dataLength = sizeof (ENetProtocolHeader);
- }
- else
- host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime;
-
- shouldCompress = 0;
- if (host -> compressor.context != NULL && host -> compressor.compress != NULL)
- {
- size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader),
- compressedSize = host -> compressor.compress (host -> compressor.context,
- & host -> buffers [1], host -> bufferCount - 1,
- originalSize,
- host -> packetData [1],
- originalSize);
- if (compressedSize > 0 && compressedSize < originalSize)
- {
- host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED;
- shouldCompress = compressedSize;
-#ifdef ENET_DEBUG_COMPRESS
- printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize);
-#endif
- }
- }
-
- if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID)
- host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT;
- header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags);
- if (host -> checksum != NULL)
- {
- enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength];
- * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0;
- host -> buffers -> dataLength += sizeof (enet_uint32);
- * checksum = host -> checksum (host -> buffers, host -> bufferCount);
- }
-
- if (shouldCompress > 0)
- {
- host -> buffers [1].data = host -> packetData [1];
- host -> buffers [1].dataLength = shouldCompress;
- host -> bufferCount = 2;
- }
-
- currentPeer -> lastSendTime = host -> serviceTime;
-
- sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount);
-
- enet_protocol_remove_sent_unreliable_commands (currentPeer);
-
- if (sentLength < 0)
- return -1;
-
- host -> totalSentData += sentLength;
- host -> totalSentPackets ++;
- }
-
- return 0;
-}
-
-/** Sends any queued packets on the host specified to its designated peers.
-
- @param host host to flush
- @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service().
- @ingroup host
-*/
-void
-enet_host_flush (ENetHost * host)
-{
- host -> serviceTime = enet_time_get ();
-
- enet_protocol_send_outgoing_commands (host, NULL, 0);
-}
-
-/** Checks for any queued events on the host and dispatches one if available.
-
- @param host host to check for events
- @param event an event structure where event details will be placed if available
- @retval > 0 if an event was dispatched
- @retval 0 if no events are available
- @retval < 0 on failure
- @ingroup host
-*/
-int
-enet_host_check_events (ENetHost * host, ENetEvent * event)
-{
- if (event == NULL) return -1;
-
- event -> type = ENET_EVENT_TYPE_NONE;
- event -> peer = NULL;
- event -> packet = NULL;
-
- return enet_protocol_dispatch_incoming_commands (host, event);
-}
-
-/** Waits for events on the host specified and shuttles packets between
- the host and its peers.
-
- @param host host to service
- @param event an event structure where event details will be placed if one occurs
- if event == NULL then no events will be delivered
- @param timeout number of milliseconds that ENet should wait for events
- @retval > 0 if an event occurred within the specified time limit
- @retval 0 if no event occurred
- @retval < 0 on failure
- @remarks enet_host_service should be called fairly regularly for adequate performance
- @ingroup host
-*/
-int
-enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
-{
- enet_uint32 waitCondition;
-
- if (event != NULL)
- {
- event -> type = ENET_EVENT_TYPE_NONE;
- event -> peer = NULL;
- event -> packet = NULL;
-
- switch (enet_protocol_dispatch_incoming_commands (host, event))
- {
- case 1:
- return 1;
-
- case -1:
-#ifdef ENET_DEBUG
- perror ("Error dispatching incoming packets");
-#endif
-
- return -1;
-
- default:
- break;
- }
- }
-
- host -> serviceTime = enet_time_get ();
-
- timeout += host -> serviceTime;
-
- do
- {
- if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
- enet_host_bandwidth_throttle (host);
-
- switch (enet_protocol_send_outgoing_commands (host, event, 1))
- {
- case 1:
- return 1;
-
- case -1:
-#ifdef ENET_DEBUG
- perror ("Error sending outgoing packets");
-#endif
-
- return -1;
-
- default:
- break;
- }
-
- switch (enet_protocol_receive_incoming_commands (host, event))
- {
- case 1:
- return 1;
-
- case -1:
-#ifdef ENET_DEBUG
- perror ("Error receiving incoming packets");
-#endif
-
- return -1;
-
- default:
- break;
- }
-
- switch (enet_protocol_send_outgoing_commands (host, event, 1))
- {
- case 1:
- return 1;
-
- case -1:
-#ifdef ENET_DEBUG
- perror ("Error sending outgoing packets");
-#endif
-
- return -1;
-
- default:
- break;
- }
-
- if (event != NULL)
- {
- switch (enet_protocol_dispatch_incoming_commands (host, event))
- {
- case 1:
- return 1;
-
- case -1:
-#ifdef ENET_DEBUG
- perror ("Error dispatching incoming packets");
-#endif
-
- return -1;
-
- default:
- break;
- }
- }
-
- if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
- return 0;
-
- do
- {
- host -> serviceTime = enet_time_get ();
-
- if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
- return 0;
-
- waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT;
-
- if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0)
- return -1;
- }
- while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT);
-
- host -> serviceTime = enet_time_get ();
- } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE);
-
- return 0;
-}
-
diff --git a/modules/enet/unix.c b/modules/enet/unix.c
deleted file mode 100644
index 3138cc04b6..0000000000
--- a/modules/enet/unix.c
+++ /dev/null
@@ -1,616 +0,0 @@
-/**
- @file unix.c
- @brief ENet Unix system specific functions
-*/
-#ifndef _WIN32
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
-#include <netdb.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <time.h>
-
-#define ENET_BUILDING_LIB 1
-#include "enet/enet.h"
-
-//@godot: added this since enet takes them fromt he build system
-#define HAS_POLL
-#define HAS_FCNTL
-#define HAS_SOCKLEN_T
-
-
-#ifdef __APPLE__
-#ifdef HAS_POLL
-#undef HAS_POLL
-#endif
-#ifndef HAS_FCNTL
-#define HAS_FCNTL 1
-#endif
-#ifndef HAS_INET_PTON
-#define HAS_INET_PTON 1
-#endif
-#ifndef HAS_INET_NTOP
-#define HAS_INET_NTOP 1
-#endif
-#ifndef HAS_MSGHDR_FLAGS
-#define HAS_MSGHDR_FLAGS 1
-#endif
-#ifndef HAS_SOCKLEN_T
-#define HAS_SOCKLEN_T 1
-#endif
-#ifndef HAS_GETADDRINFO
-#define HAS_GETADDRINFO 1
-#endif
-#ifndef HAS_GETNAMEINFO
-#define HAS_GETNAMEINFO 1
-#endif
-#endif
-
-#ifdef HAS_FCNTL
-#include <fcntl.h>
-#endif
-
-#ifdef HAS_POLL
-#include <sys/poll.h>
-#endif
-
-#ifndef HAS_SOCKLEN_T
-typedef int socklen_t;
-#endif
-
-#ifndef MSG_NOSIGNAL
-#define MSG_NOSIGNAL 0
-#endif
-
-static enet_uint32 timeBase = 0;
-
-int
-enet_initialize (void)
-{
- return 0;
-}
-
-void
-enet_deinitialize (void)
-{
-}
-
-enet_uint32
-enet_host_random_seed (void)
-{
- return (enet_uint32) time (NULL);
-}
-
-enet_uint32
-enet_time_get (void)
-{
- struct timeval timeVal;
-
- gettimeofday (& timeVal, NULL);
-
- return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase;
-}
-
-void
-enet_time_set (enet_uint32 newTimeBase)
-{
- struct timeval timeVal;
-
- gettimeofday (& timeVal, NULL);
-
- timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase;
-}
-
-int
-enet_address_set_host (ENetAddress * address, const char * name)
-{
-#ifdef HAS_GETADDRINFO
- struct addrinfo hints, * resultList = NULL, * result = NULL;
-
- memset (& hints, 0, sizeof (hints));
- hints.ai_family = AF_INET;
-
- if (getaddrinfo (name, NULL, NULL, & resultList) != 0)
- return -1;
-
- for (result = resultList; result != NULL; result = result -> ai_next)
- {
- if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in))
- {
- struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr;
-
- address -> host = sin -> sin_addr.s_addr;
-
- freeaddrinfo (resultList);
-
- return 0;
- }
- }
-
- if (resultList != NULL)
- freeaddrinfo (resultList);
-#else
- struct hostent * hostEntry = NULL;
-#ifdef HAS_GETHOSTBYNAME_R
- struct hostent hostData;
- char buffer [2048];
- int errnum;
-
-#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
- gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
-#else
- hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum);
-#endif
-#else
- hostEntry = gethostbyname (name);
-#endif
-
- if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET)
- {
- address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
-
- return 0;
- }
-#endif
-
-#ifdef HAS_INET_PTON
- if (! inet_pton (AF_INET, name, & address -> host))
-#else
- if (! inet_aton (name, (struct in_addr *) & address -> host))
-#endif
- return -1;
-
- return 0;
-}
-
-int
-enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
-{
-#ifdef HAS_INET_NTOP
- if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL)
-#else
- char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
- if (addr != NULL)
- {
- size_t addrLen = strlen(addr);
- if (addrLen >= nameLength)
- return -1;
- memcpy (name, addr, addrLen + 1);
- }
- else
-#endif
- return -1;
- return 0;
-}
-
-int
-enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
-{
-#ifdef HAS_GETNAMEINFO
- struct sockaddr_in sin;
- int err;
-
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
-
- err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD);
- if (! err)
- {
- if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength))
- return -1;
- return 0;
- }
- if (err != EAI_NONAME)
- return -1;
-#else
- struct in_addr in;
- struct hostent * hostEntry = NULL;
-#ifdef HAS_GETHOSTBYADDR_R
- struct hostent hostData;
- char buffer [2048];
- int errnum;
-
- in.s_addr = address -> host;
-
-#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
- gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum);
-#else
- hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum);
-#endif
-#else
- in.s_addr = address -> host;
-
- hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
-#endif
-
- if (hostEntry != NULL)
- {
- size_t hostLen = strlen (hostEntry -> h_name);
- if (hostLen >= nameLength)
- return -1;
- memcpy (name, hostEntry -> h_name, hostLen + 1);
- return 0;
- }
-#endif
-
- return enet_address_get_host_ip (address, name, nameLength);
-}
-
-int
-enet_socket_bind (ENetSocket socket, const ENetAddress * address)
-{
- struct sockaddr_in sin;
-
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
-
- if (address != NULL)
- {
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
- }
- else
- {
- sin.sin_port = 0;
- sin.sin_addr.s_addr = INADDR_ANY;
- }
-
- return bind (socket,
- (struct sockaddr *) & sin,
- sizeof (struct sockaddr_in));
-}
-
-int
-enet_socket_get_address (ENetSocket socket, ENetAddress * address)
-{
- struct sockaddr_in sin;
- socklen_t sinLength = sizeof (struct sockaddr_in);
-
- if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
- return -1;
-
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
-
- return 0;
-}
-
-int
-enet_socket_listen (ENetSocket socket, int backlog)
-{
- return listen (socket, backlog < 0 ? SOMAXCONN : backlog);
-}
-
-ENetSocket
-enet_socket_create (ENetSocketType type)
-{
- return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
-}
-
-int
-enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
-{
- int result = -1;
- switch (option)
- {
- case ENET_SOCKOPT_NONBLOCK:
-#ifdef HAS_FCNTL
- result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK));
-#else
- result = ioctl (socket, FIONBIO, & value);
-#endif
- break;
-
- case ENET_SOCKOPT_BROADCAST:
- result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_REUSEADDR:
- result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_RCVBUF:
- result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_SNDBUF:
- result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_RCVTIMEO:
- {
- struct timeval timeVal;
- timeVal.tv_sec = value / 1000;
- timeVal.tv_usec = (value % 1000) * 1000;
- result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval));
- break;
- }
-
- case ENET_SOCKOPT_SNDTIMEO:
- {
- struct timeval timeVal;
- timeVal.tv_sec = value / 1000;
- timeVal.tv_usec = (value % 1000) * 1000;
- result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval));
- break;
- }
-
- case ENET_SOCKOPT_NODELAY:
- result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
- break;
-
- default:
- break;
- }
- return result == -1 ? -1 : 0;
-}
-
-int
-enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
-{
- int result = -1;
- socklen_t len;
- switch (option)
- {
- case ENET_SOCKOPT_ERROR:
- len = sizeof (int);
- result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len);
- break;
-
- default:
- break;
- }
- return result == -1 ? -1 : 0;
-}
-
-int
-enet_socket_connect (ENetSocket socket, const ENetAddress * address)
-{
- struct sockaddr_in sin;
- int result;
-
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
-
- result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
- if (result == -1 && errno == EINPROGRESS)
- return 0;
-
- return result;
-}
-
-ENetSocket
-enet_socket_accept (ENetSocket socket, ENetAddress * address)
-{
- int result;
- struct sockaddr_in sin;
- socklen_t sinLength = sizeof (struct sockaddr_in);
-
- result = accept (socket,
- address != NULL ? (struct sockaddr *) & sin : NULL,
- address != NULL ? & sinLength : NULL);
-
- if (result == -1)
- return ENET_SOCKET_NULL;
-
- if (address != NULL)
- {
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
- }
-
- return result;
-}
-
-int
-enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
-{
- return shutdown (socket, (int) how);
-}
-
-void
-enet_socket_destroy (ENetSocket socket)
-{
- if (socket != -1)
- close (socket);
-}
-
-int
-enet_socket_send (ENetSocket socket,
- const ENetAddress * address,
- const ENetBuffer * buffers,
- size_t bufferCount)
-{
- struct msghdr msgHdr;
- struct sockaddr_in sin;
- int sentLength;
-
- memset (& msgHdr, 0, sizeof (struct msghdr));
-
- if (address != NULL)
- {
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
-
- msgHdr.msg_name = & sin;
- msgHdr.msg_namelen = sizeof (struct sockaddr_in);
- }
-
- msgHdr.msg_iov = (struct iovec *) buffers;
- msgHdr.msg_iovlen = bufferCount;
-
- sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL);
-
- if (sentLength == -1)
- {
- if (errno == EWOULDBLOCK)
- return 0;
-
- return -1;
- }
-
- return sentLength;
-}
-
-int
-enet_socket_receive (ENetSocket socket,
- ENetAddress * address,
- ENetBuffer * buffers,
- size_t bufferCount)
-{
- struct msghdr msgHdr;
- struct sockaddr_in sin;
- int recvLength;
-
- memset (& msgHdr, 0, sizeof (struct msghdr));
-
- if (address != NULL)
- {
- msgHdr.msg_name = & sin;
- msgHdr.msg_namelen = sizeof (struct sockaddr_in);
- }
-
- msgHdr.msg_iov = (struct iovec *) buffers;
- msgHdr.msg_iovlen = bufferCount;
-
- recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL);
-
- if (recvLength == -1)
- {
- if (errno == EWOULDBLOCK)
- return 0;
-
- return -1;
- }
-
-#ifdef HAS_MSGHDR_FLAGS
- if (msgHdr.msg_flags & MSG_TRUNC)
- return -1;
-#endif
-
- if (address != NULL)
- {
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
- }
-
- return recvLength;
-}
-
-int
-enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
-{
- struct timeval timeVal;
-
- timeVal.tv_sec = timeout / 1000;
- timeVal.tv_usec = (timeout % 1000) * 1000;
-
- return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
-}
-
-int
-enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
-{
-#ifdef HAS_POLL
- struct pollfd pollSocket;
- int pollCount;
-
- pollSocket.fd = socket;
- pollSocket.events = 0;
-
- if (* condition & ENET_SOCKET_WAIT_SEND)
- pollSocket.events |= POLLOUT;
-
- if (* condition & ENET_SOCKET_WAIT_RECEIVE)
- pollSocket.events |= POLLIN;
-
- pollCount = poll (& pollSocket, 1, timeout);
-
- if (pollCount < 0)
- {
- if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
- {
- * condition = ENET_SOCKET_WAIT_INTERRUPT;
-
- return 0;
- }
-
- return -1;
- }
-
- * condition = ENET_SOCKET_WAIT_NONE;
-
- if (pollCount == 0)
- return 0;
-
- if (pollSocket.revents & POLLOUT)
- * condition |= ENET_SOCKET_WAIT_SEND;
-
- if (pollSocket.revents & POLLIN)
- * condition |= ENET_SOCKET_WAIT_RECEIVE;
-
- return 0;
-#else
- fd_set readSet, writeSet;
- struct timeval timeVal;
- int selectCount;
-
- timeVal.tv_sec = timeout / 1000;
- timeVal.tv_usec = (timeout % 1000) * 1000;
-
- FD_ZERO (& readSet);
- FD_ZERO (& writeSet);
-
- if (* condition & ENET_SOCKET_WAIT_SEND)
- FD_SET (socket, & writeSet);
-
- if (* condition & ENET_SOCKET_WAIT_RECEIVE)
- FD_SET (socket, & readSet);
-
- selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
-
- if (selectCount < 0)
- {
- if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT)
- {
- * condition = ENET_SOCKET_WAIT_INTERRUPT;
-
- return 0;
- }
-
- return -1;
- }
-
- * condition = ENET_SOCKET_WAIT_NONE;
-
- if (selectCount == 0)
- return 0;
-
- if (FD_ISSET (socket, & writeSet))
- * condition |= ENET_SOCKET_WAIT_SEND;
-
- if (FD_ISSET (socket, & readSet))
- * condition |= ENET_SOCKET_WAIT_RECEIVE;
-
- return 0;
-#endif
-}
-
-#endif
-
diff --git a/modules/enet/win32.c b/modules/enet/win32.c
deleted file mode 100644
index 15edd7acbb..0000000000
--- a/modules/enet/win32.c
+++ /dev/null
@@ -1,435 +0,0 @@
-/**
- @file win32.c
- @brief ENet Win32 system specific functions
-*/
-#ifdef _WIN32
-
-#define ENET_BUILDING_LIB 0
-#include "enet/enet.h"
-#include <windows.h>
-#include <mmsystem.h>
-
-static enet_uint32 timeBase = 0;
-
-int
-enet_initialize (void)
-{
- WORD versionRequested = MAKEWORD (1, 1);
- WSADATA wsaData;
-
- if (WSAStartup (versionRequested, & wsaData))
- return -1;
-
- if (LOBYTE (wsaData.wVersion) != 1||
- HIBYTE (wsaData.wVersion) != 1)
- {
- WSACleanup ();
-
- return -1;
- }
-
-#ifndef WINRT_ENABLED
- timeBeginPeriod (1);
-#endif
-
- return 0;
-}
-
-void
-enet_deinitialize (void)
-{
-#ifndef WINRT_ENABLED
- timeEndPeriod (1);
-#endif
-
- WSACleanup ();
-}
-
-#ifdef WINRT_ENABLED
-enet_uint32
-timeGetTime() {
- ULONGLONG ticks = GetTickCount64();
- return (enet_uint32)ticks;
-}
-#endif
-
-
-enet_uint32
-enet_host_random_seed (void)
-{
- return (enet_uint32) timeGetTime ();
-}
-
-enet_uint32
-enet_time_get (void)
-{
- return (enet_uint32) timeGetTime () - timeBase;
-}
-
-void
-enet_time_set (enet_uint32 newTimeBase)
-{
- timeBase = (enet_uint32) timeGetTime () - newTimeBase;
-}
-
-int
-enet_address_set_host (ENetAddress * address, const char * name)
-{
- struct hostent * hostEntry;
-
- hostEntry = gethostbyname (name);
- if (hostEntry == NULL ||
- hostEntry -> h_addrtype != AF_INET)
- {
- unsigned long host = inet_addr (name);
- if (host == INADDR_NONE)
- return -1;
- address -> host = host;
- return 0;
- }
-
- address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0];
-
- return 0;
-}
-
-int
-enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength)
-{
- char * addr = inet_ntoa (* (struct in_addr *) & address -> host);
- if (addr == NULL)
- return -1;
- else
- {
- size_t addrLen = strlen(addr);
- if (addrLen >= nameLength)
- return -1;
- memcpy (name, addr, addrLen + 1);
- }
- return 0;
-}
-
-int
-enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength)
-{
- struct in_addr in;
- struct hostent * hostEntry;
-
- in.s_addr = address -> host;
-
- hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET);
- if (hostEntry == NULL)
- return enet_address_get_host_ip (address, name, nameLength);
- else
- {
- size_t hostLen = strlen (hostEntry -> h_name);
- if (hostLen >= nameLength)
- return -1;
- memcpy (name, hostEntry -> h_name, hostLen + 1);
- }
-
- return 0;
-}
-
-int
-enet_socket_bind (ENetSocket socket, const ENetAddress * address)
-{
- struct sockaddr_in sin;
-
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
-
- if (address != NULL)
- {
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
- }
- else
- {
- sin.sin_port = 0;
- sin.sin_addr.s_addr = INADDR_ANY;
- }
-
- return bind (socket,
- (struct sockaddr *) & sin,
- sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0;
-}
-
-int
-enet_socket_get_address (ENetSocket socket, ENetAddress * address)
-{
- struct sockaddr_in sin;
- int sinLength = sizeof (struct sockaddr_in);
-
- if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1)
- return -1;
-
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
-
- return 0;
-}
-
-int
-enet_socket_listen (ENetSocket socket, int backlog)
-{
- return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0;
-}
-
-ENetSocket
-enet_socket_create (ENetSocketType type)
-{
- return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0);
-}
-
-int
-enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value)
-{
- int result = SOCKET_ERROR;
- switch (option)
- {
- case ENET_SOCKOPT_NONBLOCK:
- {
- u_long nonBlocking = (u_long) value;
- result = ioctlsocket (socket, FIONBIO, & nonBlocking);
- break;
- }
-
- case ENET_SOCKOPT_BROADCAST:
- result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_REUSEADDR:
- result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_RCVBUF:
- result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_SNDBUF:
- result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_RCVTIMEO:
- result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_SNDTIMEO:
- result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int));
- break;
-
- case ENET_SOCKOPT_NODELAY:
- result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int));
- break;
-
- default:
- break;
- }
- return result == SOCKET_ERROR ? -1 : 0;
-}
-
-int
-enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value)
-{
- int result = SOCKET_ERROR, len;
- switch (option)
- {
- case ENET_SOCKOPT_ERROR:
- len = sizeof(int);
- result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len);
- break;
-
- default:
- break;
- }
- return result == SOCKET_ERROR ? -1 : 0;
-}
-
-int
-enet_socket_connect (ENetSocket socket, const ENetAddress * address)
-{
- struct sockaddr_in sin;
- int result;
-
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
-
- result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in));
- if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK)
- return -1;
-
- return 0;
-}
-
-ENetSocket
-enet_socket_accept (ENetSocket socket, ENetAddress * address)
-{
- SOCKET result;
- struct sockaddr_in sin;
- int sinLength = sizeof (struct sockaddr_in);
-
- result = accept (socket,
- address != NULL ? (struct sockaddr *) & sin : NULL,
- address != NULL ? & sinLength : NULL);
-
- if (result == INVALID_SOCKET)
- return ENET_SOCKET_NULL;
-
- if (address != NULL)
- {
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
- }
-
- return result;
-}
-
-int
-enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how)
-{
- return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0;
-}
-
-void
-enet_socket_destroy (ENetSocket socket)
-{
- if (socket != INVALID_SOCKET)
- closesocket (socket);
-}
-
-int
-enet_socket_send (ENetSocket socket,
- const ENetAddress * address,
- const ENetBuffer * buffers,
- size_t bufferCount)
-{
- struct sockaddr_in sin;
- DWORD sentLength;
-
- if (address != NULL)
- {
- memset (& sin, 0, sizeof (struct sockaddr_in));
-
- sin.sin_family = AF_INET;
- sin.sin_port = ENET_HOST_TO_NET_16 (address -> port);
- sin.sin_addr.s_addr = address -> host;
- }
-
- if (WSASendTo (socket,
- (LPWSABUF) buffers,
- (DWORD) bufferCount,
- & sentLength,
- 0,
- address != NULL ? (struct sockaddr *) & sin : NULL,
- address != NULL ? sizeof (struct sockaddr_in) : 0,
- NULL,
- NULL) == SOCKET_ERROR)
- {
- if (WSAGetLastError () == WSAEWOULDBLOCK)
- return 0;
-
- return -1;
- }
-
- return (int) sentLength;
-}
-
-int
-enet_socket_receive (ENetSocket socket,
- ENetAddress * address,
- ENetBuffer * buffers,
- size_t bufferCount)
-{
- INT sinLength = sizeof (struct sockaddr_in);
- DWORD flags = 0,
- recvLength;
- struct sockaddr_in sin;
-
- if (WSARecvFrom (socket,
- (LPWSABUF) buffers,
- (DWORD) bufferCount,
- & recvLength,
- & flags,
- address != NULL ? (struct sockaddr *) & sin : NULL,
- address != NULL ? & sinLength : NULL,
- NULL,
- NULL) == SOCKET_ERROR)
- {
- switch (WSAGetLastError ())
- {
- case WSAEWOULDBLOCK:
- case WSAECONNRESET:
- return 0;
- }
-
- return -1;
- }
-
- if (flags & MSG_PARTIAL)
- return -1;
-
- if (address != NULL)
- {
- address -> host = (enet_uint32) sin.sin_addr.s_addr;
- address -> port = ENET_NET_TO_HOST_16 (sin.sin_port);
- }
-
- return (int) recvLength;
-}
-
-int
-enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout)
-{
- struct timeval timeVal;
-
- timeVal.tv_sec = timeout / 1000;
- timeVal.tv_usec = (timeout % 1000) * 1000;
-
- return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal);
-}
-
-int
-enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout)
-{
- fd_set readSet, writeSet;
- struct timeval timeVal;
- int selectCount;
-
- timeVal.tv_sec = timeout / 1000;
- timeVal.tv_usec = (timeout % 1000) * 1000;
-
- FD_ZERO (& readSet);
- FD_ZERO (& writeSet);
-
- if (* condition & ENET_SOCKET_WAIT_SEND)
- FD_SET (socket, & writeSet);
-
- if (* condition & ENET_SOCKET_WAIT_RECEIVE)
- FD_SET (socket, & readSet);
-
- selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal);
-
- if (selectCount < 0)
- return -1;
-
- * condition = ENET_SOCKET_WAIT_NONE;
-
- if (selectCount == 0)
- return 0;
-
- if (FD_ISSET (socket, & writeSet))
- * condition |= ENET_SOCKET_WAIT_SEND;
-
- if (FD_ISSET (socket, & readSet))
- * condition |= ENET_SOCKET_WAIT_RECEIVE;
-
- return 0;
-}
-
-#endif
-
diff --git a/modules/etc1/SCsub b/modules/etc1/SCsub
new file mode 100644
index 0000000000..ea035fcde3
--- /dev/null
+++ b/modules/etc1/SCsub
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_etc1 = env_modules.Clone()
+
+# Thirdparty source files
+# Not unbundled so far since not widespread as shared library
+thirdparty_dir = "#thirdparty/rg-etc1/"
+thirdparty_sources = [
+ "rg_etc1.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_etc1.add_source_files(env.modules_sources, thirdparty_sources)
+env_etc1.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_etc1.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/etc1/config.py b/modules/etc1/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/etc1/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/etc1/image_etc.cpp b/modules/etc1/image_etc.cpp
new file mode 100644
index 0000000000..cf2384240b
--- /dev/null
+++ b/modules/etc1/image_etc.cpp
@@ -0,0 +1,201 @@
+/*************************************************************************/
+/* image_etc.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "image_etc.h"
+#include "image.h"
+#include "rg_etc1.h"
+#include "print_string.h"
+#include "os/copymem.h"
+static void _decompress_etc(Image *p_img) {
+
+ ERR_FAIL_COND(p_img->get_format()!=Image::FORMAT_ETC);
+
+ int imgw = p_img->get_width();
+ int imgh = p_img->get_height();
+ DVector<uint8_t> src=p_img->get_data();
+ DVector<uint8_t> dst;
+
+ DVector<uint8_t>::Read r = src.read();
+
+ int mmc=p_img->get_mipmaps();
+
+
+ for(int i=0;i<=mmc;i++) {
+
+ dst.resize(dst.size()+imgw*imgh*3);
+ const uint8_t *srcbr=&r[p_img->get_mipmap_offset(i)];
+ DVector<uint8_t>::Write w = dst.write();
+
+ uint8_t *wptr = &w[dst.size()-imgw*imgh*3];
+
+ int bw=MAX(imgw/4,1);
+ int bh=MAX(imgh/4,1);
+
+ for(int y=0;y<bh;y++) {
+
+ for(int x=0;x<bw;x++) {
+
+ uint8_t block[4*4*4];
+
+
+ rg_etc1::unpack_etc1_block(srcbr,(unsigned int*)block);
+ srcbr+=8;
+
+ int maxx=MIN(imgw,4);
+ int maxy=MIN(imgh,4);
+
+ for(int yy=0;yy<maxy;yy++) {
+
+ for(int xx=0;xx<maxx;xx++) {
+
+ uint32_t src_ofs = (yy*4+xx)*4;
+ uint32_t dst_ofs = ((y*4+yy)*imgw+x*4+xx)*3;
+ wptr[dst_ofs+0]=block[src_ofs+0];
+ wptr[dst_ofs+1]=block[src_ofs+1];
+ wptr[dst_ofs+2]=block[src_ofs+2];
+
+ }
+ }
+
+ }
+
+ }
+
+ imgw=MAX(1,imgw/2);
+ imgh=MAX(1,imgh/2);
+ }
+
+
+ r=DVector<uint8_t>::Read();
+ //print_line("Re Creating ETC into regular image: w "+itos(p_img->get_width())+" h "+itos(p_img->get_height())+" mm "+itos(p_img->get_mipmaps()));
+ *p_img=Image(p_img->get_width(),p_img->get_height(),p_img->get_mipmaps(),Image::FORMAT_RGB,dst);
+ if (p_img->get_mipmaps())
+ p_img->generate_mipmaps(-1,true);
+
+
+}
+
+static void _compress_etc(Image *p_img) {
+
+ Image img = *p_img;
+
+ int imgw=img.get_width(),imgh=img.get_height();
+
+ ERR_FAIL_COND( nearest_power_of_2(imgw)!=imgw || nearest_power_of_2(imgh)!=imgh );
+
+ if (img.get_format()!=Image::FORMAT_RGB)
+ img.convert(Image::FORMAT_RGB);
+
+
+ int mmc=img.get_mipmaps();
+ if (mmc==0)
+ img.generate_mipmaps(); // force mipmaps, so it works on most hardware
+
+
+ DVector<uint8_t> res_data;
+ DVector<uint8_t> dst_data;
+ DVector<uint8_t>::Read r = img.get_data().read();
+
+ int mc=0;
+
+
+ rg_etc1::etc1_pack_params pp;
+ pp.m_quality=rg_etc1::cLowQuality;
+ for(int i=0;i<=mmc;i++) {
+
+
+ int bw=MAX(imgw/4,1);
+ int bh=MAX(imgh/4,1);
+ const uint8_t *src = &r[img.get_mipmap_offset(i)];
+ int mmsize = MAX(bw,1)*MAX(bh,1)*8;
+ dst_data.resize(dst_data.size()+mmsize);
+ DVector<uint8_t>::Write w=dst_data.write();
+ uint8_t *dst = &w[dst_data.size()-mmsize];
+
+
+// print_line("bh: "+itos(bh)+" bw: "+itos(bw));
+
+ for(int y=0;y<bh;y++) {
+
+ for(int x=0;x<bw;x++) {
+
+// print_line("x: "+itos(x)+" y: "+itos(y));
+
+ uint8_t block[4*4*4];
+ zeromem(block,4*4*4);
+ uint8_t cblock[8];
+
+ int maxy = MIN(imgh,4);
+ int maxx = MIN(imgw,4);
+
+
+ for(int yy=0;yy<maxy;yy++) {
+
+ for(int xx=0;xx<maxx;xx++) {
+
+
+ uint32_t dst_ofs = (yy*4+xx)*4;
+ uint32_t src_ofs = ((y*4+yy)*imgw+x*4+xx)*3;
+ block[dst_ofs+0]=src[src_ofs+0];
+ block[dst_ofs+1]=src[src_ofs+1];
+ block[dst_ofs+2]=src[src_ofs+2];
+ block[dst_ofs+3]=255;
+
+ }
+ }
+
+ rg_etc1::pack_etc1_block(cblock, (const unsigned int*)block, pp);
+ for(int j=0;j<8;j++) {
+
+ dst[j]=cblock[j];
+ }
+
+ dst+=8;
+ }
+
+ }
+
+ imgw=MAX(1,imgw/2);
+ imgh=MAX(1,imgh/2);
+ mc++;
+
+ }
+
+ *p_img=Image(p_img->get_width(),p_img->get_height(),mc-1,Image::FORMAT_ETC,dst_data);
+
+
+}
+
+void _register_etc1_compress_func() {
+
+ rg_etc1::pack_etc1_block_init();
+ Image::_image_compress_etc_func=_compress_etc;
+ Image::_image_decompress_etc=_decompress_etc;
+
+
+}
diff --git a/modules/etc1/image_etc.h b/modules/etc1/image_etc.h
new file mode 100644
index 0000000000..edcff39bfd
--- /dev/null
+++ b/modules/etc1/image_etc.h
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* image_etc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef IMAGE_ETC1_H
+#define IMAGE_ETC1_H
+
+
+void _register_etc1_compress_func();
+
+#endif // IMAGE_ETC_H
diff --git a/modules/etc1/register_types.cpp b/modules/etc1/register_types.cpp
new file mode 100644
index 0000000000..e9eba6c864
--- /dev/null
+++ b/modules/etc1/register_types.cpp
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "image_etc.h"
+#include "texture_loader_pkm.h"
+
+static ResourceFormatPKM *resource_loader_pkm = NULL;
+
+void register_etc1_types() {
+
+ resource_loader_pkm = memnew( ResourceFormatPKM );
+ ResourceLoader::add_resource_format_loader(resource_loader_pkm);
+
+ _register_etc1_compress_func();
+}
+
+void unregister_etc1_types() {
+
+ memdelete(resource_loader_pkm);
+}
diff --git a/modules/etc1/register_types.h b/modules/etc1/register_types.h
new file mode 100644
index 0000000000..bc26699d54
--- /dev/null
+++ b/modules/etc1/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_etc1_types();
+void unregister_etc1_types();
diff --git a/modules/etc1/texture_loader_pkm.cpp b/modules/etc1/texture_loader_pkm.cpp
new file mode 100644
index 0000000000..275afc1fd6
--- /dev/null
+++ b/modules/etc1/texture_loader_pkm.cpp
@@ -0,0 +1,84 @@
+#include "texture_loader_pkm.h"
+#include "os/file_access.h"
+#include <string.h>
+
+struct ETC1Header {
+ char tag[6]; // "PKM 10"
+ uint16_t format; // Format == number of mips (== zero)
+ uint16_t texWidth; // Texture dimensions, multiple of 4 (big-endian)
+ uint16_t texHeight;
+ uint16_t origWidth; // Original dimensions (big-endian)
+ uint16_t origHeight;
+};
+
+RES ResourceFormatPKM::load(const String &p_path, const String& p_original_path, Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_CANT_OPEN;
+
+ Error err;
+ FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err);
+ if (!f)
+ return RES();
+
+ FileAccessRef fref(f);
+ if (r_error)
+ *r_error=ERR_FILE_CORRUPT;
+
+ ERR_EXPLAIN("Unable to open PKM texture file: "+p_path);
+ ERR_FAIL_COND_V(err!=OK,RES());
+
+ // big endian
+ f->set_endian_swap(true);
+
+ ETC1Header h;
+ ERR_EXPLAIN("Invalid or Unsupported PKM texture file: "+p_path);
+ f->get_buffer((uint8_t *) &h.tag, sizeof(h.tag));
+ if(strncmp(h.tag, "PKM 10", sizeof(h.tag)))
+ ERR_FAIL_V(RES());
+
+ h.format = f->get_16();
+ h.texWidth = f->get_16();
+ h.texHeight = f->get_16();
+ h.origWidth = f->get_16();
+ h.origHeight = f->get_16();
+
+ DVector<uint8_t> src_data;
+
+ uint32_t size = h.texWidth * h.texHeight / 2;
+ src_data.resize(size);
+ DVector<uint8_t>::Write wb = src_data.write();
+ f->get_buffer(wb.ptr(),size);
+ wb=DVector<uint8_t>::Write();
+
+ int mipmaps = h.format;
+ int width = h.origWidth;
+ int height = h.origHeight;
+
+ Image img(width,height,mipmaps,Image::FORMAT_ETC,src_data);
+
+ Ref<ImageTexture> texture = memnew( ImageTexture );
+ texture->create_from_image(img);
+
+ if (r_error)
+ *r_error=OK;
+
+ return texture;
+}
+
+void ResourceFormatPKM::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("pkm");
+}
+
+bool ResourceFormatPKM::handles_type(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Texture");
+}
+
+String ResourceFormatPKM::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="pkm")
+ return "ImageTexture";
+ return "";
+}
diff --git a/modules/etc1/texture_loader_pkm.h b/modules/etc1/texture_loader_pkm.h
new file mode 100644
index 0000000000..5788716d9f
--- /dev/null
+++ b/modules/etc1/texture_loader_pkm.h
@@ -0,0 +1,18 @@
+#ifndef TEXTURE_LOADER_PKM_H
+#define TEXTURE_LOADER_PKM_H
+
+#include "scene/resources/texture.h"
+#include "io/resource_loader.h"
+
+class ResourceFormatPKM : public ResourceFormatLoader{
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+ virtual ~ResourceFormatPKM() {}
+};
+
+#endif // TEXTURE_LOADER_PKM_H
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
new file mode 100644
index 0000000000..1f759dee9a
--- /dev/null
+++ b/modules/freetype/SCsub
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+
+Import('env')
+
+# Not building in a separate env as core needs it
+
+# Thirdparty source files
+if (env["freetype"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/freetype/"
+ thirdparty_sources = [
+ "src/autofit/autofit.c",
+ "src/base/ftapi.c",
+ "src/base/ftbase.c",
+ "src/base/ftbbox.c",
+ "src/base/ftbdf.c",
+ "src/base/ftbitmap.c",
+ "src/base/ftcid.c",
+ "src/base/ftdebug.c",
+ "src/base/ftfntfmt.c",
+ "src/base/ftfstype.c",
+ "src/base/ftgasp.c",
+ "src/base/ftglyph.c",
+ "src/base/ftgxval.c",
+ "src/base/ftinit.c",
+ "src/base/ftlcdfil.c",
+ "src/base/ftmm.c",
+ "src/base/ftotval.c",
+ "src/base/ftpatent.c",
+ "src/base/ftpfr.c",
+ "src/base/ftpic.c",
+ "src/base/ftstroke.c",
+ "src/base/ftsynth.c",
+ "src/base/ftsystem.c",
+ "src/base/fttype1.c",
+ "src/base/ftwinfnt.c",
+ "src/bdf/bdf.c",
+ "src/cache/ftcache.c",
+ "src/cff/cff.c",
+ "src/cid/type1cid.c",
+ "src/gxvalid/gxvalid.c",
+ "src/otvalid/otvalid.c",
+ "src/pcf/pcf.c",
+ "src/pfr/pfr.c",
+ "src/psaux/psaux.c",
+ "src/pshinter/pshinter.c",
+ "src/psnames/psnames.c",
+ "src/raster/raster.c",
+ "src/sfnt/sfnt.c",
+ "src/smooth/smooth.c",
+ "src/truetype/truetype.c",
+ "src/type1/type1.c",
+ "src/type42/type42.c",
+ "src/winfonts/winfnt.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ # Include header for WinRT to fix build issues
+ if "platform" in env and env["platform"] == "winrt":
+ env.Append(CCFLAGS = ['/FI', '"modules/freetype/winrtdef.h"'])
+
+ env.Append(CPPPATH = [thirdparty_dir, thirdparty_dir + "/include"])
+
+ # also requires libpng headers
+ if (env["libpng"] != "system"): # builtin
+ env.Append(CPPPATH = ["#thirdparty/libpng"])
+
+ """ FIXME: Remove this commented code if Windows can handle the monolithic lib
+ # fix for Windows' shell miserably failing on long lines, split in two libraries
+ half1 = []
+ half2 = []
+ for x in thirdparty_sources:
+ if (x.find("src/base") != -1 and x.find("src/sfnt") != -1):
+ half1.append(x)
+ else:
+ half2.append(x)
+
+ lib = env.Library("freetype_builtin1", half2)
+ env.Append(LIBS = [lib])
+ lib = env.Library("freetype_builtin2", half1)
+ env.Append(LIBS = [lib])
+ """
+
+ lib = env.Library("freetype_builtin", thirdparty_sources)
+ env.Append(LIBS = [lib])
+
+# Godot source files
+env.add_source_files(env.modules_sources, "*.cpp")
+env.Append(CCFLAGS = ['-DFREETYPE_ENABLED'])
+
+Export('env')
diff --git a/modules/freetype/config.py b/modules/freetype/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/freetype/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/freetype/register_types.cpp b/modules/freetype/register_types.cpp
new file mode 100644
index 0000000000..2b9f47f54c
--- /dev/null
+++ b/modules/freetype/register_types.cpp
@@ -0,0 +1,33 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+void register_freetype_types() {}
+
+void unregister_freetype_types() {}
diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h
new file mode 100644
index 0000000000..326cd2e6ea
--- /dev/null
+++ b/modules/freetype/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_freetype_types();
+void unregister_freetype_types();
diff --git a/modules/freetype/winrtdef.h b/modules/freetype/winrtdef.h
new file mode 100644
index 0000000000..69c6baf532
--- /dev/null
+++ b/modules/freetype/winrtdef.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* winrtdef.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// "generic" is a reserved keyword in C++/CX code
+// this avoids the errors in the variable name from Freetype code
+#define generic freetype_generic
diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub
index 403fe68f66..0882406761 100644
--- a/modules/gdscript/SCsub
+++ b/modules/gdscript/SCsub
@@ -1,5 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp
index 2e2cbe7b29..b75b13551e 100644
--- a/modules/gdscript/gd_compiler.cpp
+++ b/modules/gdscript/gd_compiler.cpp
@@ -1005,12 +1005,12 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
switch(s->type) {
case GDParser::Node::TYPE_NEWLINE: {
-
+#ifdef DEBUG_ENABLED
const GDParser::NewLineNode *nl = static_cast<const GDParser::NewLineNode*>(s);
codegen.opcodes.push_back(GDFunction::OPCODE_LINE);
codegen.opcodes.push_back(nl->line);
codegen.current_line=nl->line;
-
+#endif
} break;
case GDParser::Node::TYPE_CONTROL_FLOW: {
// try subblocks
@@ -1201,8 +1201,10 @@ Error GDCompiler::_parse_block(CodeGen& codegen,const GDParser::BlockNode *p_blo
codegen.opcodes.push_back(ret);
} break;
case GDParser::Node::TYPE_BREAKPOINT: {
+#ifdef DEBUG_ENABLED
// try subblocks
codegen.opcodes.push_back(GDFunction::OPCODE_BREAKPOINT);
+#endif
} break;
case GDParser::Node::TYPE_LOCAL_VAR: {
diff --git a/modules/gdscript/gd_functions.cpp b/modules/gdscript/gd_functions.cpp
index a565e866d0..d3f7dcd35f 100644
--- a/modules/gdscript/gd_functions.cpp
+++ b/modules/gdscript/gd_functions.cpp
@@ -88,6 +88,7 @@ const char *GDFunctions::get_func_name(Function p_func) {
"convert",
"typeof",
"type_exists",
+ "char",
"str",
"print",
"printt",
@@ -538,6 +539,12 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_ret = ObjectTypeDB::type_exists(*p_args[0]);
} break;
+ case TEXT_CHAR: {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_NUM(0);
+ CharType result[2] = {*p_args[0], 0};
+ r_ret=String(result);
+ } break;
case TEXT_STR: {
String str;
@@ -840,8 +847,9 @@ void GDFunctions::call(Function p_func,const Variant **p_args,int p_arg_count,Va
r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument=0;
r_ret=Variant();
+ } else {
+ r_ret=ResourceLoader::load(*p_args[0]);
}
- r_ret=ResourceLoader::load(*p_args[0]);
} break;
case INST2DICT: {
@@ -1133,6 +1141,7 @@ bool GDFunctions::is_deterministic(Function p_func) {
case TYPE_CONVERT:
case TYPE_OF:
case TYPE_EXISTS:
+ case TEXT_CHAR:
case TEXT_STR:
case COLOR8:
// enable for debug only, otherwise not desirable - case GEN_RANGE:
@@ -1403,6 +1412,13 @@ MethodInfo GDFunctions::get_info(Function p_func) {
return mi;
} break;
+ case TEXT_CHAR: {
+
+ MethodInfo mi("char",PropertyInfo(Variant::INT,"ascii"));
+ mi.return_val.type=Variant::STRING;
+ return mi;
+
+ } break;
case TEXT_STR: {
MethodInfo mi("str",PropertyInfo(Variant::NIL,"what"),PropertyInfo(Variant::NIL,"..."));
diff --git a/modules/gdscript/gd_functions.h b/modules/gdscript/gd_functions.h
index c78956fe20..f444bb3b5b 100644
--- a/modules/gdscript/gd_functions.h
+++ b/modules/gdscript/gd_functions.h
@@ -82,6 +82,7 @@ public:
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
+ TEXT_CHAR,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINT_TABBED,
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 8f4f5ef4ca..434f918355 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -121,6 +121,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
tokenizer->advance();
} else {
+ parenthesis ++;
int argidx=0;
while(true) {
@@ -165,6 +166,7 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
}
}
+ parenthesis --;
}
return true;
@@ -364,18 +366,23 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
OperatorNode *yield = alloc_node<OperatorNode>();
yield->op=OperatorNode::OP_YIELD;
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
expr=yield;
tokenizer->advance();
} else {
+ parenthesis ++;
+
Node *object = _parse_and_reduce_expression(p_parent,p_static);
if (!object)
return NULL;
yield->arguments.push_back(object);
if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) {
-
_set_error("Expected ',' after first argument of 'yield'");
return NULL;
}
@@ -403,11 +410,12 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
yield->arguments.push_back(signal);
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
-
_set_error("Expected ')' after second argument of 'yield'");
return NULL;
}
+ parenthesis --;
+
tokenizer->advance();
expr=yield;
@@ -1705,6 +1713,7 @@ void GDParser::_parse_block(BlockNode *p_block,bool p_static) {
case GDTokenizer::TK_CF_IF: {
tokenizer->advance();
+
Node *condition = _parse_and_reduce_expression(p_block,p_static);
if (!condition) {
if (_recover_from_completion()) {
@@ -2305,6 +2314,11 @@ void GDParser::_parse_class(ClassNode *p_class) {
bool defaulting=false;
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_PR_VAR) {
tokenizer->advance(); //var before the identifier is allowed
@@ -2357,6 +2371,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
default_values.push_back(on);
}
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
continue;
@@ -2398,6 +2416,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
//has arguments
+ parenthesis ++;
while(true) {
Node *arg = _parse_and_reduce_expression(p_class,_static);
@@ -2415,6 +2434,7 @@ void GDParser::_parse_class(ClassNode *p_class) {
break;
}
+ parenthesis --;
}
tokenizer->advance();
@@ -2478,6 +2498,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
while(true) {
+ if (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ continue;
+ }
if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -2493,6 +2517,10 @@ void GDParser::_parse_class(ClassNode *p_class) {
sig.arguments.push_back(tokenizer->get_token_identifier());
tokenizer->advance();
+ while (tokenizer->get_token()==GDTokenizer::TK_NEWLINE) {
+ tokenizer->advance();
+ }
+
if (tokenizer->get_token()==GDTokenizer::TK_COMMA) {
tokenizer->advance();
} else if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
@@ -3101,6 +3129,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
member._export.type=cn->value.get_type();
member._export.usage|=PROPERTY_USAGE_SCRIPT_VARIABLE;
+ if (cn->value.get_type()==Variant::OBJECT) {
+ Object *obj = cn->value;
+ Resource *res = obj->cast_to<Resource>();
+ if(res==NULL) {
+ _set_error("Exported constant not a type or resource.");
+ return;
+ }
+ member._export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ member._export.hint_string=res->get_type();
+ }
}
}
#ifdef TOOLS_ENABLED
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 2041ec12ad..39c4530d96 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -282,7 +282,7 @@ void GDTokenizerText::_advance() {
case '\n': {
line++;
INCPOS(1);
- column=0;
+ column=1;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
i++;
@@ -303,7 +303,7 @@ void GDTokenizerText::_advance() {
}
}
INCPOS(1);
- column=0;
+ column=1;
line++;
int i=0;
while(GETCHAR(i)==' ' || GETCHAR(i)=='\t') {
@@ -335,7 +335,7 @@ void GDTokenizerText::_advance() {
break;
} else if (_code[pos]=='\n') {
new_line++;
- new_col=0;
+ new_col=1;
} else {
new_col++;
}
@@ -358,7 +358,7 @@ void GDTokenizerText::_advance() {
}
}
INCPOS(1);
- column=0;
+ column=1;
line++;
continue;
@@ -654,7 +654,7 @@ void GDTokenizerText::_advance() {
} else {
if (CharType(GETCHAR(i))=='\n') {
line++;
- column=0;
+ column=1;
}
str+=CharType(GETCHAR(i));
@@ -943,7 +943,7 @@ void GDTokenizerText::set_code(const String& p_code) {
}
code_pos=0;
line=1; //it is stand-ar-ized that lines begin in 1 in code..
- column=0;
+ column=1; //the same holds for columns
tk_rb_pos=0;
error_flag=false;
last_error="";
diff --git a/modules/gridmap/SCsub b/modules/gridmap/SCsub
index 211a043468..0882406761 100644
--- a/modules/gridmap/SCsub
+++ b/modules/gridmap/SCsub
@@ -1,3 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/ik/SCsub b/modules/ik/SCsub
index 211a043468..0882406761 100644
--- a/modules/ik/SCsub
+++ b/modules/ik/SCsub
@@ -1,3 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
new file mode 100644
index 0000000000..28fb81895d
--- /dev/null
+++ b/modules/jpg/SCsub
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_jpg = env_modules.Clone()
+
+# Thirdparty source files
+# Not unbundled for now as they are not commonly available as shared library
+thirdparty_dir = "#thirdparty/jpeg-compressor/"
+thirdparty_sources = [
+ "jpgd.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_jpg.add_source_files(env.modules_sources, thirdparty_sources)
+env_jpg.Append(CPPPATH = [thirdparty_dir])
+
+# Godot's own source files
+env_jpg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/jpg/config.py b/modules/jpg/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/jpg/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
new file mode 100644
index 0000000000..03c3b19fc0
--- /dev/null
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* image_loader_jpegd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "image_loader_jpegd.h"
+
+#include "print_string.h"
+#include "os/os.h"
+
+#include <jpgd.h>
+#include <string.h>
+
+
+Error jpeg_load_image_from_buffer(Image *p_image,const uint8_t* p_buffer, int p_buffer_len) {
+
+ jpgd::jpeg_decoder_mem_stream mem_stream(p_buffer,p_buffer_len);
+
+ jpgd::jpeg_decoder decoder(&mem_stream);
+
+ if (decoder.get_error_code() != jpgd::JPGD_SUCCESS) {
+ return ERR_CANT_OPEN;
+ }
+
+ const int image_width = decoder.get_width();
+ const int image_height = decoder.get_height();
+ int comps = decoder.get_num_components();
+ if (comps==3)
+ comps=4; //weird
+
+ if (decoder.begin_decoding() != jpgd::JPGD_SUCCESS)
+ return ERR_FILE_CORRUPT;
+
+ const int dst_bpl = image_width * comps;
+
+ DVector<uint8_t> data;
+
+ data.resize(dst_bpl * image_height);
+
+ DVector<uint8_t>::Write dw = data.write();
+
+ jpgd::uint8 *pImage_data = (jpgd::uint8*)dw.ptr();
+
+ for (int y = 0; y < image_height; y++)
+ {
+ const jpgd::uint8* pScan_line;
+ jpgd::uint scan_line_len;
+ if (decoder.decode((const void**)&pScan_line, &scan_line_len) != jpgd::JPGD_SUCCESS)
+ {
+ return ERR_FILE_CORRUPT;
+ }
+
+ jpgd::uint8 *pDst = pImage_data + y * dst_bpl;
+ memcpy(pDst, pScan_line, dst_bpl);
+
+
+ }
+
+
+ //all good
+
+ Image::Format fmt;
+ if (comps==1)
+ fmt=Image::FORMAT_GRAYSCALE;
+ else
+ fmt=Image::FORMAT_RGBA;
+
+ dw = DVector<uint8_t>::Write();
+ p_image->create(image_width,image_height,0,fmt,data);
+
+ return OK;
+
+}
+
+
+Error ImageLoaderJPG::load_image(Image *p_image,FileAccess *f) {
+
+
+ DVector<uint8_t> src_image;
+ int src_image_len = f->get_len();
+ ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
+ src_image.resize(src_image_len);
+
+ DVector<uint8_t>::Write w = src_image.write();
+
+ f->get_buffer(&w[0],src_image_len);
+
+ f->close();
+
+
+ Error err = jpeg_load_image_from_buffer(p_image,w.ptr(),src_image_len);
+
+ w = DVector<uint8_t>::Write();
+
+ return err;
+
+}
+
+void ImageLoaderJPG::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("jpg");
+ p_extensions->push_back("jpeg");
+}
+
+
+static Image _jpegd_mem_loader_func(const uint8_t* p_png,int p_size) {
+
+ Image img;
+ Error err = jpeg_load_image_from_buffer(&img,p_png,p_size);
+ if (err)
+ ERR_PRINT("Couldn't initialize ImageLoaderJPG with the given resource.");
+
+ return img;
+}
+
+ImageLoaderJPG::ImageLoaderJPG() {
+
+ Image::_jpg_mem_loader_func=_jpegd_mem_loader_func;
+}
+
+
diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h
new file mode 100644
index 0000000000..2c52309ab1
--- /dev/null
+++ b/modules/jpg/image_loader_jpegd.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* image_loader_jpegd.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef IMAGE_LOADER_JPG_H
+#define IMAGE_LOADER_JPG_H
+
+#include "io/image_loader.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class ImageLoaderJPG : public ImageFormatLoader {
+
+
+public:
+
+ virtual Error load_image(Image *p_image,FileAccess *f);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ ImageLoaderJPG();
+};
+
+
+
+#endif
diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp
new file mode 100644
index 0000000000..a648423cdf
--- /dev/null
+++ b/modules/jpg/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "image_loader_jpegd.h"
+
+static ImageLoaderJPG *image_loader_jpg = NULL;
+
+void register_jpg_types() {
+
+ image_loader_jpg = memnew( ImageLoaderJPG );
+ ImageLoader::add_image_format_loader(image_loader_jpg);
+}
+
+void unregister_jpg_types() {
+
+ memdelete( image_loader_jpg );
+}
diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h
new file mode 100644
index 0000000000..0e06c4ff81
--- /dev/null
+++ b/modules/jpg/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_jpg_types();
+void unregister_jpg_types();
diff --git a/modules/mpc/SCsub b/modules/mpc/SCsub
new file mode 100644
index 0000000000..09f0c05daa
--- /dev/null
+++ b/modules/mpc/SCsub
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_mpc = env_modules.Clone()
+
+# Thirdparty source files
+if (env["libmpcdec"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/libmpcdec/"
+ thirdparty_sources = [
+ "huffman.c",
+ "mpc_bits_reader.c",
+ "mpc_decoder.c",
+ "mpc_demux.c",
+ "mpc_reader.c",
+ "requant.c",
+ "streaminfo.c",
+ "synth_filter.c",
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_mpc.add_source_files(env.modules_sources, thirdparty_sources)
+ env_mpc.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_mpc.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/mpc/audio_stream_mpc.cpp b/modules/mpc/audio_stream_mpc.cpp
new file mode 100644
index 0000000000..9713eb3c77
--- /dev/null
+++ b/modules/mpc/audio_stream_mpc.cpp
@@ -0,0 +1,414 @@
+/*************************************************************************/
+/* audio_stream_mpc.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_stream_mpc.h"
+
+
+Error AudioStreamPlaybackMPC::_open_file() {
+
+ if (f) {
+ memdelete(f);
+ f=NULL;
+ }
+ Error err;
+ //printf("mpc open file %ls\n", file.c_str());
+ f=FileAccess::open(file,FileAccess::READ,&err);
+
+ if (err) {
+ f=NULL;
+ ERR_FAIL_V(err);
+ return err;
+ }
+
+ //printf("file size is %i\n", f->get_len());
+ //f->seek_end(0);
+ streamlen=f->get_len();
+ //f->seek(0);
+ if (streamlen<=0) {
+ memdelete(f);
+ f=NULL;
+ ERR_FAIL_V(ERR_INVALID_DATA);
+ }
+
+ data_ofs=0;
+ if (preload) {
+
+ data.resize(streamlen);
+ DVector<uint8_t>::Write w = data.write();
+ f->get_buffer(&w[0],streamlen);
+ memdelete(f);
+ f=NULL;
+
+ }
+
+ return OK;
+}
+
+void AudioStreamPlaybackMPC::_close_file() {
+
+ if (f) {
+ memdelete(f);
+ f=NULL;
+ }
+ data.resize(0);
+ streamlen=0;
+ data_ofs=0;
+}
+
+int AudioStreamPlaybackMPC::_read_file(void *p_dst,int p_bytes) {
+
+ if (f)
+ return f->get_buffer((uint8_t*)p_dst,p_bytes);
+
+ DVector<uint8_t>::Read r = data.read();
+ if (p_bytes+data_ofs > streamlen) {
+ p_bytes=streamlen-data_ofs;
+ }
+
+ copymem(p_dst,&r[data_ofs],p_bytes);
+ //print_line("read file: "+itos(p_bytes));
+ data_ofs+=p_bytes;
+ return p_bytes;
+}
+
+bool AudioStreamPlaybackMPC::_seek_file(int p_pos){
+
+ if (p_pos<0 || p_pos>streamlen)
+ return false;
+
+ if (f) {
+ f->seek(p_pos);
+ return true;
+ }
+
+ //print_line("read file to: "+itos(p_pos));
+ data_ofs=p_pos;
+ return true;
+
+}
+int AudioStreamPlaybackMPC::_tell_file() const{
+
+ if (f)
+ return f->get_pos();
+
+ //print_line("tell file, get: "+itos(data_ofs));
+ return data_ofs;
+
+}
+
+int AudioStreamPlaybackMPC::_sizeof_file() const{
+
+ //print_line("sizeof file, get: "+itos(streamlen));
+ return streamlen;
+}
+
+bool AudioStreamPlaybackMPC::_canseek_file() const{
+
+ //print_line("canseek file, get true");
+ return true;
+}
+
+/////////////////////
+
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes) {
+
+ AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
+ return smpc->_read_file(p_dst,p_bytes);
+}
+
+mpc_bool_t AudioStreamPlaybackMPC::_mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset) {
+
+ AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
+ return smpc->_seek_file(p_offset);
+
+}
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_tell(mpc_reader *p_reader) {
+
+ AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
+ return smpc->_tell_file();
+
+}
+mpc_int32_t AudioStreamPlaybackMPC::_mpc_get_size(mpc_reader *p_reader) {
+
+ AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
+ return smpc->_sizeof_file();
+
+
+}
+mpc_bool_t AudioStreamPlaybackMPC::_mpc_canseek(mpc_reader *p_reader) {
+
+ AudioStreamPlaybackMPC *smpc=(AudioStreamPlaybackMPC *)p_reader->data;
+ return smpc->_canseek_file();
+}
+
+
+
+
+int AudioStreamPlaybackMPC::mix(int16_t* p_bufer,int p_frames) {
+
+ if (!active || paused)
+ return 0;
+
+ int todo=p_frames;
+
+ while(todo>MPC_DECODER_BUFFER_LENGTH/si.channels) {
+
+ mpc_frame_info frame;
+
+ frame.buffer=sample_buffer;
+
+ mpc_status err = mpc_demux_decode(demux, &frame);
+ if (frame.bits!=-1) {
+
+ int16_t *dst_buff = p_bufer;
+
+#ifdef MPC_FIXED_POINT
+
+ for( int i = 0; i < frame.samples * si.channels; i++) {
+ int tmp = sample_buffer[i] >> MPC_FIXED_POINT_FRACTPART;
+ if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
+ if (tmp < -(1 << 15)) tmp = -(1 << 15);
+ dst_buff[i] = tmp;
+ }
+#else
+ for( int i = 0; i < frame.samples * si.channels; i++) {
+
+ int tmp = Math::fast_ftoi(sample_buffer[i]*32767.0);
+ if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
+ if (tmp < -(1 << 15)) tmp = -(1 << 15);
+ dst_buff[i] = tmp;
+
+ }
+
+#endif
+
+ int frames = frame.samples;
+ p_bufer+=si.channels*frames;
+ todo-=frames;
+ } else {
+
+ if (err != MPC_STATUS_OK) {
+
+ stop();
+ ERR_PRINT("Error decoding MPC");
+ break;
+ } else {
+
+ //finished
+ if (!loop) {
+ stop();
+ break;
+ } else {
+
+
+ loops++;
+ mpc_demux_exit(demux);
+ _seek_file(0);
+ demux = mpc_demux_init(&reader);
+ //do loop somehow
+
+ }
+ }
+ }
+ }
+
+ return p_frames-todo;
+}
+
+Error AudioStreamPlaybackMPC::_reload() {
+
+ ERR_FAIL_COND_V(demux!=NULL, ERR_FILE_ALREADY_IN_USE);
+
+ Error err = _open_file();
+ ERR_FAIL_COND_V(err!=OK,ERR_CANT_OPEN);
+
+ demux = mpc_demux_init(&reader);
+ ERR_FAIL_COND_V(!demux,ERR_CANT_CREATE);
+ mpc_demux_get_info(demux, &si);
+
+ return OK;
+}
+
+void AudioStreamPlaybackMPC::set_file(const String& p_file) {
+
+ file=p_file;
+
+ Error err = _open_file();
+ ERR_FAIL_COND(err!=OK);
+ demux = mpc_demux_init(&reader);
+ ERR_FAIL_COND(!demux);
+ mpc_demux_get_info(demux, &si);
+ stream_min_size=MPC_DECODER_BUFFER_LENGTH*2/si.channels;
+ stream_rate=si.sample_freq;
+ stream_channels=si.channels;
+
+ mpc_demux_exit(demux);
+ demux=NULL;
+ _close_file();
+
+}
+
+
+String AudioStreamPlaybackMPC::get_file() const {
+
+ return file;
+}
+
+
+void AudioStreamPlaybackMPC::play(float p_offset) {
+
+
+ if (active)
+ stop();
+ active=false;
+
+ Error err = _open_file();
+ ERR_FAIL_COND(err!=OK);
+ if (_reload()!=OK)
+ return;
+ active=true;
+ loops=0;
+
+}
+
+void AudioStreamPlaybackMPC::stop() {
+
+
+ if (!active)
+ return;
+ if (demux) {
+ mpc_demux_exit(demux);
+ demux=NULL;
+ }
+ _close_file();
+ active=false;
+
+}
+bool AudioStreamPlaybackMPC::is_playing() const {
+
+ return active;
+}
+
+
+void AudioStreamPlaybackMPC::set_loop(bool p_enable) {
+
+ loop=p_enable;
+}
+bool AudioStreamPlaybackMPC::has_loop() const {
+
+ return loop;
+}
+
+float AudioStreamPlaybackMPC::get_length() const {
+
+ return 0;
+}
+
+String AudioStreamPlaybackMPC::get_stream_name() const {
+
+ return "";
+}
+
+int AudioStreamPlaybackMPC::get_loop_count() const {
+
+ return 0;
+}
+
+float AudioStreamPlaybackMPC::get_pos() const {
+
+ return 0;
+}
+void AudioStreamPlaybackMPC::seek_pos(float p_time) {
+
+
+}
+
+
+void AudioStreamPlaybackMPC::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_file","name"),&AudioStreamPlaybackMPC::set_file);
+ ObjectTypeDB::bind_method(_MD("get_file"),&AudioStreamPlaybackMPC::get_file);
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::STRING,"file",PROPERTY_HINT_FILE,"mpc"), _SCS("set_file"), _SCS("get_file"));
+
+}
+
+AudioStreamPlaybackMPC::AudioStreamPlaybackMPC() {
+
+ preload=false;
+ f=NULL;
+ streamlen=0;
+ data_ofs=0;
+ active=false;
+ paused=false;
+ loop=false;
+ demux=NULL;
+ reader.data=this;
+ reader.read=_mpc_read;
+ reader.seek=_mpc_seek;
+ reader.tell=_mpc_tell;
+ reader.get_size=_mpc_get_size;
+ reader.canseek=_mpc_canseek;
+ loops=0;
+
+}
+
+AudioStreamPlaybackMPC::~AudioStreamPlaybackMPC() {
+
+ stop();
+
+ if (f)
+ memdelete(f);
+}
+
+
+
+RES ResourceFormatLoaderAudioStreamMPC::load(const String &p_path, const String& p_original_path, Error *r_error) {
+ if (r_error)
+ *r_error=OK; //streamed so it will always work..
+ AudioStreamMPC *mpc_stream = memnew(AudioStreamMPC);
+ mpc_stream->set_file(p_path);
+ return Ref<AudioStreamMPC>(mpc_stream);
+}
+
+void ResourceFormatLoaderAudioStreamMPC::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("mpc");
+}
+bool ResourceFormatLoaderAudioStreamMPC::handles_type(const String& p_type) const {
+
+ return (p_type=="AudioStream") || (p_type=="AudioStreamMPC");
+}
+
+String ResourceFormatLoaderAudioStreamMPC::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="mpc")
+ return "AudioStreamMPC";
+ return "";
+}
+
diff --git a/modules/mpc/audio_stream_mpc.h b/modules/mpc/audio_stream_mpc.h
new file mode 100644
index 0000000000..c982bdc358
--- /dev/null
+++ b/modules/mpc/audio_stream_mpc.h
@@ -0,0 +1,146 @@
+/*************************************************************************/
+/* audio_stream_mpc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_STREAM_MPC_H
+#define AUDIO_STREAM_MPC_H
+
+#include "io/resource_loader.h"
+#include "os/file_access.h"
+#include "os/thread_safe.h"
+#include "scene/resources/audio_stream.h"
+
+#include <mpc/mpcdec.h>
+
+class AudioStreamPlaybackMPC : public AudioStreamPlayback {
+
+ OBJ_TYPE( AudioStreamPlaybackMPC, AudioStreamPlayback );
+
+ bool preload;
+ FileAccess *f;
+ String file;
+ DVector<uint8_t> data;
+ int data_ofs;
+ int streamlen;
+
+
+ bool active;
+ bool paused;
+ bool loop;
+ int loops;
+
+ // mpc
+ mpc_reader reader;
+ mpc_demux* demux;
+ mpc_streaminfo si;
+ MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
+
+ static mpc_int32_t _mpc_read(mpc_reader *p_reader,void *p_dst, mpc_int32_t p_bytes);
+ static mpc_bool_t _mpc_seek(mpc_reader *p_reader,mpc_int32_t p_offset);
+ static mpc_int32_t _mpc_tell(mpc_reader *p_reader);
+ static mpc_int32_t _mpc_get_size(mpc_reader *p_reader);
+ static mpc_bool_t _mpc_canseek(mpc_reader *p_reader);
+
+ int stream_min_size;
+ int stream_rate;
+ int stream_channels;
+
+protected:
+ Error _open_file();
+ void _close_file();
+ int _read_file(void *p_dst,int p_bytes);
+ bool _seek_file(int p_pos);
+ int _tell_file() const;
+ int _sizeof_file() const;
+ bool _canseek_file() const;
+
+
+ Error _reload();
+ static void _bind_methods();
+
+public:
+
+ void set_file(const String& p_file);
+ String get_file() const;
+
+ virtual void play(float p_offset=0);
+ virtual void stop();
+ virtual bool is_playing() const;
+
+
+ virtual void set_loop(bool p_enable);
+ virtual bool has_loop() const;
+
+ virtual float get_length() const;
+
+ virtual String get_stream_name() const;
+
+ virtual int get_loop_count() const;
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+ virtual int get_channels() const { return stream_channels; }
+ virtual int get_mix_rate() const { return stream_rate; }
+
+ virtual int get_minimum_buffer_size() const { return stream_min_size; }
+ virtual int mix(int16_t* p_bufer,int p_frames);
+
+ virtual void set_loop_restart_time(float p_time) { }
+
+ AudioStreamPlaybackMPC();
+ ~AudioStreamPlaybackMPC();
+};
+
+class AudioStreamMPC : public AudioStream {
+
+ OBJ_TYPE( AudioStreamMPC, AudioStream );
+
+ String file;
+public:
+
+ Ref<AudioStreamPlayback> instance_playback() {
+ Ref<AudioStreamPlaybackMPC> pb = memnew( AudioStreamPlaybackMPC );
+ pb->set_file(file);
+ return pb;
+ }
+
+ void set_file(const String& p_file) { file=p_file; }
+
+
+};
+
+class ResourceFormatLoaderAudioStreamMPC : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+#endif // AUDIO_STREAM_MPC_H
diff --git a/modules/mpc/config.py b/modules/mpc/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/mpc/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/mpc/register_types.cpp b/modules/mpc/register_types.cpp
new file mode 100644
index 0000000000..f6a1f59dca
--- /dev/null
+++ b/modules/mpc/register_types.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "audio_stream_mpc.h"
+
+static ResourceFormatLoaderAudioStreamMPC* mpc_stream_loader = NULL;
+
+void register_mpc_types() {
+
+ mpc_stream_loader=memnew( ResourceFormatLoaderAudioStreamMPC );
+ ResourceLoader::add_resource_format_loader(mpc_stream_loader);
+ ObjectTypeDB::register_type<AudioStreamMPC>();
+}
+
+void unregister_mpc_types() {
+
+ memdelete( mpc_stream_loader );
+}
diff --git a/modules/mpc/register_types.h b/modules/mpc/register_types.h
new file mode 100644
index 0000000000..3d0661ed62
--- /dev/null
+++ b/modules/mpc/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_mpc_types();
+void unregister_mpc_types();
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
new file mode 100644
index 0000000000..2e1fe2e0c0
--- /dev/null
+++ b/modules/ogg/SCsub
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_ogg = env_modules.Clone()
+
+# Thirdparty source files
+if (env["libogg"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/libogg/"
+ thirdparty_sources = [
+ "bitwise.c",
+ "framing.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_ogg.add_source_files(env.modules_sources, thirdparty_sources)
+ env_ogg.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_ogg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/ogg/config.py b/modules/ogg/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/ogg/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp
new file mode 100644
index 0000000000..823ca510ca
--- /dev/null
+++ b/modules/ogg/register_types.cpp
@@ -0,0 +1,35 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+// Dummy module as libogg is needed by other modules (vorbis, theora, opus, ...)
+
+void register_ogg_types() {}
+
+void unregister_ogg_types() {}
diff --git a/modules/ogg/register_types.h b/modules/ogg/register_types.h
new file mode 100644
index 0000000000..b5268a1df4
--- /dev/null
+++ b/modules/ogg/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_ogg_types();
+void unregister_ogg_types();
diff --git a/modules/openssl/SCsub b/modules/openssl/SCsub
new file mode 100644
index 0000000000..2327cf483c
--- /dev/null
+++ b/modules/openssl/SCsub
@@ -0,0 +1,687 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_openssl = env_modules.Clone()
+
+# Thirdparty source files
+if (env["openssl"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/openssl/"
+
+ thirdparty_sources = [
+ "ssl/t1_lib.c",
+ "ssl/t1_ext.c",
+ "ssl/s3_srvr.c",
+ "ssl/t1_enc.c",
+ "ssl/t1_meth.c",
+ "ssl/s23_clnt.c",
+ "ssl/ssl_asn1.c",
+ "ssl/tls_srp.c",
+ "ssl/kssl.c",
+ "ssl/d1_both.c",
+ "ssl/t1_clnt.c",
+ "ssl/bio_ssl.c",
+ "ssl/d1_srtp.c",
+ "ssl/t1_reneg.c",
+ "ssl/ssl_cert.c",
+ "ssl/s3_lib.c",
+ "ssl/d1_srvr.c",
+ "ssl/s23_meth.c",
+ "ssl/ssl_stat.c",
+ "ssl/ssl_err.c",
+ "ssl/ssl_algs.c",
+ "ssl/s3_cbc.c",
+ "ssl/d1_clnt.c",
+ "ssl/s3_pkt.c",
+ "ssl/d1_meth.c",
+ "ssl/s3_both.c",
+ "ssl/s2_enc.c",
+ "ssl/s3_meth.c",
+ "ssl/s3_enc.c",
+ "ssl/s23_pkt.c",
+ "ssl/s2_pkt.c",
+ "ssl/d1_pkt.c",
+ "ssl/ssl_rsa.c",
+ "ssl/s23_srvr.c",
+ "ssl/s2_meth.c",
+ "ssl/s3_clnt.c",
+ "ssl/s23_lib.c",
+ "ssl/t1_srvr.c",
+ "ssl/ssl_lib.c",
+ "ssl/ssl_txt.c",
+ "ssl/s2_srvr.c",
+ "ssl/ssl_sess.c",
+ "ssl/s2_clnt.c",
+ "ssl/d1_lib.c",
+ "ssl/s2_lib.c",
+ "ssl/ssl_err2.c",
+ "ssl/ssl_ciph.c",
+ "crypto/dsa/dsa_lib.c",
+ "crypto/dsa/dsa_pmeth.c",
+ "crypto/dsa/dsa_ossl.c",
+ "crypto/dsa/dsa_gen.c",
+ "crypto/dsa/dsa_asn1.c",
+ "crypto/dsa/dsa_prn.c",
+ "crypto/dsa/dsa_sign.c",
+ "crypto/dsa/dsa_key.c",
+ "crypto/dsa/dsa_vrf.c",
+ "crypto/dsa/dsa_err.c",
+ "crypto/dsa/dsa_ameth.c",
+ "crypto/dsa/dsa_depr.c",
+ "crypto/x509/x509_lu.c",
+ "crypto/x509/x509cset.c",
+ "crypto/x509/x509_set.c",
+ "crypto/x509/x509_d2.c",
+ "crypto/x509/x509_txt.c",
+ "crypto/x509/x509rset.c",
+ "crypto/x509/by_dir.c",
+ "crypto/x509/x509_vpm.c",
+ "crypto/x509/x509_vfy.c",
+ "crypto/x509/x509_trs.c",
+ "crypto/x509/by_file.c",
+ "crypto/x509/x509_obj.c",
+ "crypto/x509/x509spki.c",
+ "crypto/x509/x509_v3.c",
+ "crypto/x509/x509_req.c",
+ "crypto/x509/x509_att.c",
+ "crypto/x509/x_all.c",
+ "crypto/x509/x509_ext.c",
+ "crypto/x509/x509type.c",
+ "crypto/x509/x509_def.c",
+ "crypto/x509/x509_err.c",
+ "crypto/x509/x509name.c",
+ "crypto/x509/x509_r2x.c",
+ "crypto/x509/x509_cmp.c",
+ "crypto/asn1/x_pkey.c",
+ "crypto/asn1/a_gentm.c",
+ "crypto/asn1/x_sig.c",
+ "crypto/asn1/t_req.c",
+ "crypto/asn1/t_pkey.c",
+ "crypto/asn1/p8_pkey.c",
+ "crypto/asn1/a_i2d_fp.c",
+ "crypto/asn1/x_val.c",
+ "crypto/asn1/f_string.c",
+ "crypto/asn1/p5_pbe.c",
+ "crypto/asn1/bio_ndef.c",
+ "crypto/asn1/a_bool.c",
+ "crypto/asn1/asn1_gen.c",
+ "crypto/asn1/x_algor.c",
+ "crypto/asn1/bio_asn1.c",
+ "crypto/asn1/asn_mime.c",
+ "crypto/asn1/t_x509.c",
+ "crypto/asn1/a_strex.c",
+ "crypto/asn1/x_nx509.c",
+ "crypto/asn1/asn1_err.c",
+ "crypto/asn1/x_crl.c",
+ "crypto/asn1/a_print.c",
+ "crypto/asn1/a_type.c",
+ "crypto/asn1/tasn_new.c",
+ "crypto/asn1/n_pkey.c",
+ "crypto/asn1/x_bignum.c",
+ "crypto/asn1/asn_pack.c",
+ "crypto/asn1/evp_asn1.c",
+ "crypto/asn1/t_bitst.c",
+ "crypto/asn1/x_req.c",
+ "crypto/asn1/a_time.c",
+ "crypto/asn1/x_name.c",
+ "crypto/asn1/x_pubkey.c",
+ "crypto/asn1/tasn_typ.c",
+ "crypto/asn1/asn_moid.c",
+ "crypto/asn1/a_utctm.c",
+ "crypto/asn1/asn1_lib.c",
+ "crypto/asn1/x_x509a.c",
+ "crypto/asn1/a_set.c",
+ "crypto/asn1/t_crl.c",
+ "crypto/asn1/p5_pbev2.c",
+ "crypto/asn1/tasn_enc.c",
+ "crypto/asn1/a_mbstr.c",
+ "crypto/asn1/tasn_dec.c",
+ "crypto/asn1/x_x509.c",
+ "crypto/asn1/a_octet.c",
+ "crypto/asn1/x_long.c",
+ "crypto/asn1/a_bytes.c",
+ "crypto/asn1/t_x509a.c",
+ "crypto/asn1/a_enum.c",
+ "crypto/asn1/a_int.c",
+ "crypto/asn1/tasn_prn.c",
+ "crypto/asn1/i2d_pr.c",
+ "crypto/asn1/a_utf8.c",
+ "crypto/asn1/t_spki.c",
+ "crypto/asn1/a_digest.c",
+ "crypto/asn1/a_dup.c",
+ "crypto/asn1/i2d_pu.c",
+ "crypto/asn1/a_verify.c",
+ "crypto/asn1/f_enum.c",
+ "crypto/asn1/a_sign.c",
+ "crypto/asn1/d2i_pr.c",
+ "crypto/asn1/asn1_par.c",
+ "crypto/asn1/x_spki.c",
+ "crypto/asn1/a_d2i_fp.c",
+ "crypto/asn1/f_int.c",
+ "crypto/asn1/x_exten.c",
+ "crypto/asn1/tasn_utl.c",
+ "crypto/asn1/nsseq.c",
+ "crypto/asn1/a_bitstr.c",
+ "crypto/asn1/x_info.c",
+ "crypto/asn1/a_strnid.c",
+ "crypto/asn1/a_object.c",
+ "crypto/asn1/tasn_fre.c",
+ "crypto/asn1/d2i_pu.c",
+ "crypto/asn1/ameth_lib.c",
+ "crypto/asn1/x_attrib.c",
+ "crypto/evp/m_sha.c",
+ "crypto/evp/e_camellia.c",
+ "crypto/evp/e_aes.c",
+ "crypto/evp/bio_b64.c",
+ "crypto/evp/m_sigver.c",
+ "crypto/evp/m_wp.c",
+ "crypto/evp/m_sha1.c",
+ "crypto/evp/p_seal.c",
+ "crypto/evp/c_alld.c",
+ "crypto/evp/p5_crpt.c",
+ "crypto/evp/e_rc4.c",
+ "crypto/evp/m_ecdsa.c",
+ "crypto/evp/bio_enc.c",
+ "crypto/evp/e_des3.c",
+ "crypto/evp/m_null.c",
+ "crypto/evp/bio_ok.c",
+ "crypto/evp/pmeth_gn.c",
+ "crypto/evp/e_rc5.c",
+ "crypto/evp/e_rc2.c",
+ "crypto/evp/p_dec.c",
+ "crypto/evp/p_verify.c",
+ "crypto/evp/e_rc4_hmac_md5.c",
+ "crypto/evp/pmeth_lib.c",
+ "crypto/evp/m_ripemd.c",
+ "crypto/evp/m_md5.c",
+ "crypto/evp/e_bf.c",
+ "crypto/evp/p_enc.c",
+ "crypto/evp/m_dss.c",
+ "crypto/evp/bio_md.c",
+ "crypto/evp/evp_pbe.c",
+ "crypto/evp/e_seed.c",
+ "crypto/evp/e_cast.c",
+ "crypto/evp/p_open.c",
+ "crypto/evp/p5_crpt2.c",
+ "crypto/evp/m_dss1.c",
+ "crypto/evp/names.c",
+ "crypto/evp/evp_acnf.c",
+ "crypto/evp/e_des.c",
+ "crypto/evp/evp_cnf.c",
+ "crypto/evp/evp_lib.c",
+ "crypto/evp/digest.c",
+ "crypto/evp/evp_err.c",
+ "crypto/evp/evp_enc.c",
+ "crypto/evp/e_old.c",
+ "crypto/evp/c_all.c",
+ "crypto/evp/m_md2.c",
+ "crypto/evp/e_xcbc_d.c",
+ "crypto/evp/pmeth_fn.c",
+ "crypto/evp/p_lib.c",
+ "crypto/evp/evp_key.c",
+ "crypto/evp/encode.c",
+ "crypto/evp/e_aes_cbc_hmac_sha1.c",
+ "crypto/evp/e_aes_cbc_hmac_sha256.c",
+ "crypto/evp/m_mdc2.c",
+ "crypto/evp/e_null.c",
+ "crypto/evp/p_sign.c",
+ "crypto/evp/e_idea.c",
+ "crypto/evp/c_allc.c",
+ "crypto/evp/evp_pkey.c",
+ "crypto/evp/m_md4.c",
+ "crypto/ex_data.c",
+ "crypto/pkcs12/p12_p8e.c",
+ "crypto/pkcs12/p12_crt.c",
+ "crypto/pkcs12/p12_utl.c",
+ "crypto/pkcs12/p12_attr.c",
+ "crypto/pkcs12/p12_npas.c",
+ "crypto/pkcs12/p12_decr.c",
+ "crypto/pkcs12/p12_init.c",
+ "crypto/pkcs12/p12_kiss.c",
+ "crypto/pkcs12/p12_add.c",
+ "crypto/pkcs12/p12_p8d.c",
+ "crypto/pkcs12/p12_mutl.c",
+ "crypto/pkcs12/p12_crpt.c",
+ "crypto/pkcs12/pk12err.c",
+ "crypto/pkcs12/p12_asn.c",
+ "crypto/pkcs12/p12_key.c",
+ "crypto/ecdh/ech_key.c",
+ "crypto/ecdh/ech_ossl.c",
+ "crypto/ecdh/ech_lib.c",
+ "crypto/ecdh/ech_err.c",
+ "crypto/ecdh/ech_kdf.c",
+ "crypto/o_str.c",
+ "crypto/conf/conf_api.c",
+ "crypto/conf/conf_err.c",
+ "crypto/conf/conf_def.c",
+ "crypto/conf/conf_lib.c",
+ "crypto/conf/conf_mall.c",
+ "crypto/conf/conf_sap.c",
+ "crypto/conf/conf_mod.c",
+ "crypto/ebcdic.c",
+ "crypto/ecdsa/ecs_lib.c",
+ "crypto/ecdsa/ecs_asn1.c",
+ "crypto/ecdsa/ecs_ossl.c",
+ "crypto/ecdsa/ecs_vrf.c",
+ "crypto/ecdsa/ecs_sign.c",
+ "crypto/ecdsa/ecs_err.c",
+ "crypto/dso/dso_win32.c",
+ "crypto/dso/dso_lib.c",
+ "crypto/dso/dso_dlfcn.c",
+ "crypto/dso/dso_dl.c",
+ "crypto/dso/dso_beos.c",
+ "crypto/dso/dso_null.c",
+ "crypto/dso/dso_vms.c",
+ "crypto/dso/dso_err.c",
+ "crypto/dso/dso_openssl.c",
+ "crypto/cryptlib.c",
+ "crypto/md5/md5_one.c",
+ "crypto/md5/md5_dgst.c",
+ "crypto/pkcs7/pkcs7err.c",
+ "crypto/pkcs7/pk7_smime.c",
+ "crypto/pkcs7/bio_pk7.c",
+ "crypto/pkcs7/pk7_mime.c",
+ "crypto/pkcs7/pk7_lib.c",
+ "crypto/pkcs7/pk7_asn1.c",
+ "crypto/pkcs7/pk7_doit.c",
+ "crypto/pkcs7/pk7_attr.c",
+ "crypto/md4/md4_one.c",
+ "crypto/md4/md4_dgst.c",
+ "crypto/o_dir.c",
+ "crypto/buffer/buf_err.c",
+ "crypto/buffer/buf_str.c",
+ "crypto/buffer/buffer.c",
+ "crypto/cms/cms_lib.c",
+ "crypto/cms/cms_io.c",
+ "crypto/cms/cms_err.c",
+ "crypto/cms/cms_dd.c",
+ "crypto/cms/cms_smime.c",
+ "crypto/cms/cms_att.c",
+ "crypto/cms/cms_pwri.c",
+ "crypto/cms/cms_cd.c",
+ "crypto/cms/cms_sd.c",
+ "crypto/cms/cms_asn1.c",
+ "crypto/cms/cms_env.c",
+ "crypto/cms/cms_enc.c",
+ "crypto/cms/cms_ess.c",
+ "crypto/cms/cms_kari.c",
+ "crypto/mem_dbg.c",
+ "crypto/uid.c",
+ "crypto/stack/stack.c",
+ "crypto/ec/ec_ameth.c",
+ "crypto/ec/ec_err.c",
+ "crypto/ec/ec_lib.c",
+ "crypto/ec/ec_curve.c",
+ "crypto/ec/ec_oct.c",
+ "crypto/ec/ec_asn1.c",
+ "crypto/ec/ecp_oct.c",
+ "crypto/ec/ec_print.c",
+ "crypto/ec/ec2_smpl.c",
+ "crypto/ec/ecp_nistp224.c",
+ "crypto/ec/ec2_oct.c",
+ "crypto/ec/eck_prn.c",
+ "crypto/ec/ec_key.c",
+ "crypto/ec/ecp_nist.c",
+ "crypto/ec/ec_check.c",
+ "crypto/ec/ecp_smpl.c",
+ "crypto/ec/ec2_mult.c",
+ "crypto/ec/ecp_mont.c",
+ "crypto/ec/ecp_nistp521.c",
+ "crypto/ec/ec_mult.c",
+ "crypto/ec/ecp_nistputil.c",
+ "crypto/ec/ec_pmeth.c",
+ "crypto/ec/ec_cvt.c",
+ "crypto/ec/ecp_nistp256.c",
+ "crypto/krb5/krb5_asn.c",
+ "crypto/hmac/hmac.c",
+ "crypto/hmac/hm_ameth.c",
+ "crypto/hmac/hm_pmeth.c",
+ "crypto/comp/c_rle.c",
+ "crypto/comp/c_zlib.c",
+ "crypto/comp/comp_lib.c",
+ "crypto/comp/comp_err.c",
+ "crypto/des/fcrypt.c",
+ "crypto/des/str2key.c",
+ "crypto/des/cbc_cksm.c",
+ "crypto/des/des_enc.c",
+ "crypto/des/ofb_enc.c",
+ "crypto/des/read2pwd.c",
+ "crypto/des/ecb3_enc.c",
+ "crypto/des/rand_key.c",
+ "crypto/des/cfb64ede.c",
+ "crypto/des/rpc_enc.c",
+ "crypto/des/ofb64ede.c",
+ "crypto/des/qud_cksm.c",
+ "crypto/des/enc_writ.c",
+ "crypto/des/set_key.c",
+ "crypto/des/xcbc_enc.c",
+ "crypto/des/fcrypt_b.c",
+ "crypto/des/ede_cbcm_enc.c",
+ "crypto/des/des_old2.c",
+ "crypto/des/cfb_enc.c",
+ "crypto/des/ecb_enc.c",
+ "crypto/des/enc_read.c",
+ "crypto/des/des_old.c",
+ "crypto/des/ofb64enc.c",
+ "crypto/des/pcbc_enc.c",
+ "crypto/des/cbc_enc.c",
+ "crypto/des/cfb64enc.c",
+ "crypto/lhash/lh_stats.c",
+ "crypto/lhash/lhash.c",
+ "crypto/x509v3/v3_genn.c",
+ "crypto/x509v3/pcy_cache.c",
+ "crypto/x509v3/v3_sxnet.c",
+ "crypto/x509v3/v3_scts.c",
+ "crypto/x509v3/v3err.c",
+ "crypto/x509v3/v3_conf.c",
+ "crypto/x509v3/v3_utl.c",
+ "crypto/x509v3/v3_akeya.c",
+ "crypto/x509v3/v3_lib.c",
+ "crypto/x509v3/pcy_lib.c",
+ "crypto/x509v3/v3_cpols.c",
+ "crypto/x509v3/v3_ia5.c",
+ "crypto/x509v3/v3_bitst.c",
+ "crypto/x509v3/v3_skey.c",
+ "crypto/x509v3/v3_info.c",
+ "crypto/x509v3/v3_asid.c",
+ "crypto/x509v3/pcy_tree.c",
+ "crypto/x509v3/v3_pcons.c",
+ "crypto/x509v3/v3_bcons.c",
+ "crypto/x509v3/v3_pku.c",
+ "crypto/x509v3/v3_ocsp.c",
+ "crypto/x509v3/pcy_map.c",
+ "crypto/x509v3/v3_ncons.c",
+ "crypto/x509v3/v3_purp.c",
+ "crypto/x509v3/v3_enum.c",
+ "crypto/x509v3/v3_pmaps.c",
+ "crypto/x509v3/pcy_node.c",
+ "crypto/x509v3/v3_pcia.c",
+ "crypto/x509v3/v3_crld.c",
+ "crypto/x509v3/v3_pci.c",
+ "crypto/x509v3/v3_akey.c",
+ "crypto/x509v3/v3_addr.c",
+ "crypto/x509v3/v3_int.c",
+ "crypto/x509v3/v3_alt.c",
+ "crypto/x509v3/v3_extku.c",
+ "crypto/x509v3/v3_prn.c",
+ "crypto/x509v3/pcy_data.c",
+ "crypto/aes/aes_ofb.c",
+ "crypto/aes/aes_ctr.c",
+ "crypto/aes/aes_ecb.c",
+ "crypto/aes/aes_cfb.c",
+ "crypto/aes/aes_wrap.c",
+ "crypto/aes/aes_ige.c",
+ "crypto/aes/aes_misc.c",
+ "crypto/pqueue/pqueue.c",
+ "crypto/sha/sha_one.c",
+ "crypto/sha/sha_dgst.c",
+ "crypto/sha/sha512.c",
+ "crypto/sha/sha1_one.c",
+ "crypto/sha/sha1dgst.c",
+ "crypto/sha/sha256.c",
+ "crypto/whrlpool/wp_dgst.c",
+ "crypto/objects/obj_xref.c",
+ "crypto/objects/o_names.c",
+ "crypto/objects/obj_err.c",
+ "crypto/objects/obj_dat.c",
+ "crypto/objects/obj_lib.c",
+ "crypto/mem.c",
+ "crypto/fips_ers.c",
+ "crypto/o_fips.c",
+ "crypto/engine/eng_rdrand.c",
+ "crypto/engine/eng_err.c",
+ "crypto/engine/tb_ecdsa.c",
+ "crypto/engine/tb_rsa.c",
+ "crypto/engine/tb_cipher.c",
+ "crypto/engine/tb_dsa.c",
+ "crypto/engine/eng_lib.c",
+ "crypto/engine/tb_asnmth.c",
+ "crypto/engine/tb_ecdh.c",
+ "crypto/engine/tb_dh.c",
+ "crypto/engine/tb_store.c",
+ "crypto/engine/eng_init.c",
+ "crypto/engine/eng_cnf.c",
+ "crypto/engine/eng_all.c",
+ "crypto/engine/tb_digest.c",
+ "crypto/engine/tb_pkmeth.c",
+ "crypto/engine/eng_table.c",
+ "crypto/engine/eng_ctrl.c",
+ "crypto/engine/eng_list.c",
+ "crypto/engine/eng_cryptodev.c",
+ "crypto/engine/eng_pkey.c",
+ "crypto/engine/tb_rand.c",
+ "crypto/engine/eng_openssl.c",
+ "crypto/engine/eng_fat.c",
+ "crypto/engine/eng_dyn.c",
+ "crypto/ts/ts_rsp_verify.c",
+ "crypto/ts/ts_req_print.c",
+ "crypto/ts/ts_verify_ctx.c",
+ "crypto/ts/ts_req_utils.c",
+ "crypto/ts/ts_err.c",
+ "crypto/ts/ts_rsp_print.c",
+ "crypto/ts/ts_rsp_utils.c",
+ "crypto/ts/ts_lib.c",
+ "crypto/ts/ts_conf.c",
+ "crypto/ts/ts_asn1.c",
+ "crypto/ts/ts_rsp_sign.c",
+ "crypto/ocsp/ocsp_ext.c",
+ "crypto/ocsp/ocsp_cl.c",
+ "crypto/ocsp/ocsp_ht.c",
+ "crypto/ocsp/ocsp_lib.c",
+ "crypto/ocsp/ocsp_srv.c",
+ "crypto/ocsp/ocsp_vfy.c",
+ "crypto/ocsp/ocsp_err.c",
+ "crypto/ocsp/ocsp_prn.c",
+ "crypto/ocsp/ocsp_asn.c",
+ "crypto/bf/bf_cfb64.c",
+ "crypto/bf/bf_ecb.c",
+ "crypto/bf/bf_enc.c",
+ "crypto/bf/bf_skey.c",
+ "crypto/bf/bf_ofb64.c",
+ "crypto/idea/i_skey.c",
+ "crypto/idea/i_ofb64.c",
+ "crypto/idea/i_cbc.c",
+ "crypto/idea/i_ecb.c",
+ "crypto/idea/i_cfb64.c",
+ "crypto/cmac/cm_ameth.c",
+ "crypto/cmac/cmac.c",
+ "crypto/cmac/cm_pmeth.c",
+ "crypto/dh/dh_lib.c",
+ "crypto/dh/dh_key.c",
+ "crypto/dh/dh_asn1.c",
+ "crypto/dh/dh_depr.c",
+ "crypto/dh/dh_pmeth.c",
+ "crypto/dh/dh_prn.c",
+ "crypto/dh/dh_gen.c",
+ "crypto/dh/dh_ameth.c",
+ "crypto/dh/dh_check.c",
+ "crypto/dh/dh_err.c",
+ "crypto/dh/dh_kdf.c",
+ "crypto/dh/dh_rfc5114.c",
+ "crypto/modes/ccm128.c",
+ "crypto/modes/ofb128.c",
+ "crypto/modes/cts128.c",
+ "crypto/modes/ctr128.c",
+ "crypto/modes/gcm128.c",
+ "crypto/modes/cbc128.c",
+ "crypto/modes/cfb128.c",
+ "crypto/modes/xts128.c",
+ "crypto/modes/wrap128.c",
+ "crypto/camellia/cmll_cfb.c",
+ "crypto/camellia/cmll_ecb.c",
+ "crypto/camellia/cmll_utl.c",
+ "crypto/camellia/cmll_misc.c",
+ "crypto/camellia/cmll_ofb.c",
+ "crypto/camellia/cmll_ctr.c",
+ "crypto/seed/seed_ecb.c",
+ "crypto/seed/seed_cbc.c",
+ "crypto/seed/seed.c",
+ "crypto/seed/seed_ofb.c",
+ "crypto/seed/seed_cfb.c",
+ "crypto/txt_db/txt_db.c",
+ "crypto/cpt_err.c",
+ "crypto/pem/pem_pk8.c",
+ "crypto/pem/pem_lib.c",
+ "crypto/pem/pem_sign.c",
+ "crypto/pem/pem_all.c",
+ "crypto/pem/pem_info.c",
+ "crypto/pem/pem_pkey.c",
+ "crypto/pem/pem_seal.c",
+ "crypto/pem/pem_err.c",
+ "crypto/pem/pem_xaux.c",
+ "crypto/pem/pvkfmt.c",
+ "crypto/pem/pem_x509.c",
+ "crypto/pem/pem_oth.c",
+ "crypto/rand/rand_lib.c",
+ "crypto/rand/randfile.c",
+ "crypto/rand/rand_os2.c",
+ "crypto/rand/rand_unix.c",
+ "crypto/rand/rand_nw.c",
+ "crypto/rand/md_rand.c",
+ "crypto/rand/rand_err.c",
+ "crypto/rand/rand_win.c",
+ "crypto/rand/rand_egd.c",
+ "crypto/cversion.c",
+ "crypto/cast/c_ecb.c",
+ "crypto/cast/c_skey.c",
+ "crypto/cast/c_ofb64.c",
+ "crypto/cast/c_enc.c",
+ "crypto/cast/c_cfb64.c",
+ "crypto/o_time.c",
+ "crypto/mdc2/mdc2dgst.c",
+ "crypto/mdc2/mdc2_one.c",
+ "crypto/rc4/rc4_utl.c",
+ "crypto/ui/ui_compat.c",
+ "crypto/ui/ui_util.c",
+ "crypto/ui/ui_lib.c",
+ "crypto/ui/ui_err.c",
+ "crypto/ui/ui_openssl.c",
+ "crypto/bio/bf_buff.c",
+ "crypto/bio/bss_null.c",
+ "crypto/bio/bss_acpt.c",
+ "crypto/bio/bss_conn.c",
+ "crypto/bio/bss_fd.c",
+ "crypto/bio/bf_null.c",
+ "crypto/bio/bio_err.c",
+ "crypto/bio/bss_sock.c",
+ "crypto/bio/bss_mem.c",
+ "crypto/bio/b_dump.c",
+ "crypto/bio/b_print.c",
+ "crypto/bio/b_sock.c",
+ "crypto/bio/bss_dgram.c",
+ "crypto/bio/bf_nbio.c",
+ "crypto/bio/bio_lib.c",
+ "crypto/bio/bss_file.c",
+ "crypto/bio/bss_bio.c",
+ "crypto/bio/bss_log.c",
+ "crypto/bio/bio_cb.c",
+ "crypto/o_init.c",
+ "crypto/rc2/rc2_skey.c",
+ "crypto/rc2/rc2_cbc.c",
+ "crypto/rc2/rc2cfb64.c",
+ "crypto/rc2/rc2_ecb.c",
+ "crypto/rc2/rc2ofb64.c",
+ "crypto/bn/bn_x931p.c",
+ "crypto/bn/bn_blind.c",
+ "crypto/bn/bn_gf2m.c",
+ "crypto/bn/bn_const.c",
+ "crypto/bn/bn_sqr.c",
+ "crypto/bn/bn_nist.c",
+ "crypto/bn/bn_rand.c",
+ "crypto/bn/bn_err.c",
+ "crypto/bn/bn_div.c",
+ "crypto/bn/bn_kron.c",
+ "crypto/bn/bn_ctx.c",
+ "crypto/bn/bn_shift.c",
+ "crypto/bn/bn_mod.c",
+ "crypto/bn/bn_exp2.c",
+ "crypto/bn/bn_word.c",
+ "crypto/bn/bn_add.c",
+ "crypto/bn/bn_exp.c",
+ "crypto/bn/bn_mont.c",
+ "crypto/bn/bn_print.c",
+ "crypto/bn/bn_mul.c",
+ "crypto/bn/bn_prime.c",
+ "crypto/bn/bn_depr.c",
+ "crypto/bn/bn_gcd.c",
+ "crypto/bn/bn_mpi.c",
+ "crypto/bn/bn_sqrt.c",
+ "crypto/bn/bn_recp.c",
+ "crypto/bn/bn_lib.c",
+ "crypto/ripemd/rmd_dgst.c",
+ "crypto/ripemd/rmd_one.c",
+ "crypto/rsa/rsa_x931.c",
+ "crypto/rsa/rsa_depr.c",
+ "crypto/rsa/rsa_saos.c",
+ "crypto/rsa/rsa_crpt.c",
+ "crypto/rsa/rsa_pss.c",
+ "crypto/rsa/rsa_oaep.c",
+ "crypto/rsa/rsa_null.c",
+ "crypto/rsa/rsa_gen.c",
+ "crypto/rsa/rsa_prn.c",
+ "crypto/rsa/rsa_pmeth.c",
+ "crypto/rsa/rsa_asn1.c",
+ "crypto/rsa/rsa_ssl.c",
+ "crypto/rsa/rsa_ameth.c",
+ "crypto/rsa/rsa_pk1.c",
+ "crypto/rsa/rsa_err.c",
+ "crypto/rsa/rsa_lib.c",
+ "crypto/rsa/rsa_none.c",
+ "crypto/rsa/rsa_chk.c",
+ "crypto/rsa/rsa_eay.c",
+ "crypto/rsa/rsa_sign.c",
+ "crypto/srp/srp_lib.c",
+ "crypto/srp/srp_vfy.c",
+ "crypto/err/err.c",
+ "crypto/err/err_prn.c",
+ "crypto/err/err_all.c",
+ "crypto/mem_clr.c",
+ "crypto/rc4/rc4_skey.c",
+ "crypto/rc4/rc4_enc.c",
+ "crypto/camellia/camellia.c",
+ "crypto/camellia/cmll_cbc.c",
+ #"crypto/aes/aes_x86core.c",
+ "crypto/aes/aes_core.c",
+ "crypto/aes/aes_cbc.c",
+ "crypto/whrlpool/wp_block.c",
+ "crypto/bn/bn_asm.c",
+ ]
+
+ if "platform" in env and env["platform"] == "winrt":
+ thirdparty_sources += ['winrt.cpp']
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_openssl.add_source_files(env.modules_sources, thirdparty_sources)
+
+ # FIXME: Clone the environment to make a env_openssl and not pollute the modules env
+ thirdparty_include_paths = [
+ "",
+ "crypto",
+ "crypto/asn1",
+ "crypto/evp",
+ "crypto/modes",
+ "openssl",
+ ]
+ env_openssl.Append(CPPPATH = [thirdparty_dir + "/" + dir for dir in thirdparty_include_paths])
+
+ env_openssl.Append(CPPFLAGS = ["-DOPENSSL_NO_ASM", "-DOPENSSL_THREADS", "-DL_ENDIAN"])
+
+ # Workaround for compilation error with GCC/Clang when -Werror is too greedy (GH-4517)
+ import os
+ import methods
+ if not (os.name=="nt" and os.getenv("VCINSTALLDIR")): # not Windows and not MSVC
+ env_openssl.Append(CFLAGS = ["-Wno-error=implicit-function-declaration"])
+
+
+# Module sources
+env_openssl.add_source_files(env.modules_sources, "*.cpp")
+env_openssl.add_source_files(env.modules_sources, "*.c")
+
+# platform/winrt need to know openssl is available, pass to main env
+if "platform" in env and env["platform"] == "winrt":
+ env.Append(CPPPATH = [thirdparty_dir])
+ env.Append(CPPFLAGS = ['-DOPENSSL_ENABLED']);
+
+Export('env')
diff --git a/modules/openssl/config.py b/modules/openssl/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/openssl/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/openssl/curl_hostcheck.c b/modules/openssl/curl_hostcheck.c
new file mode 100644
index 0000000000..feef232619
--- /dev/null
+++ b/modules/openssl/curl_hostcheck.c
@@ -0,0 +1,217 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* This file is an amalgamation of hostcheck.c and most of rawstr.c
+ from cURL. The contents of the COPYING file mentioned above are:
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2013, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+*/
+
+#include "curl_hostcheck.h"
+#include <string.h>
+
+/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
+ its behavior is altered by the current locale. */
+static char Curl_raw_toupper(char in)
+{
+ switch (in) {
+ case 'a':
+ return 'A';
+ case 'b':
+ return 'B';
+ case 'c':
+ return 'C';
+ case 'd':
+ return 'D';
+ case 'e':
+ return 'E';
+ case 'f':
+ return 'F';
+ case 'g':
+ return 'G';
+ case 'h':
+ return 'H';
+ case 'i':
+ return 'I';
+ case 'j':
+ return 'J';
+ case 'k':
+ return 'K';
+ case 'l':
+ return 'L';
+ case 'm':
+ return 'M';
+ case 'n':
+ return 'N';
+ case 'o':
+ return 'O';
+ case 'p':
+ return 'P';
+ case 'q':
+ return 'Q';
+ case 'r':
+ return 'R';
+ case 's':
+ return 'S';
+ case 't':
+ return 'T';
+ case 'u':
+ return 'U';
+ case 'v':
+ return 'V';
+ case 'w':
+ return 'W';
+ case 'x':
+ return 'X';
+ case 'y':
+ return 'Y';
+ case 'z':
+ return 'Z';
+ }
+ return in;
+}
+
+/*
+ * Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * some further explanation to why this function is necessary.
+ *
+ * The function is capable of comparing a-z case insensitively even for
+ * non-ascii.
+ */
+
+static int Curl_raw_equal(const char *first, const char *second)
+{
+ while(*first && *second) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+ /* get out of the loop as soon as they don't match */
+ break;
+ first++;
+ second++;
+ }
+ /* we do the comparison here (possibly again), just to make sure that if the
+ loop above is skipped because one of the strings reached zero, we must not
+ return this as a successful match */
+ return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
+}
+
+static int Curl_raw_nequal(const char *first, const char *second, size_t max)
+{
+ while(*first && *second && max) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) {
+ break;
+ }
+ max--;
+ first++;
+ second++;
+ }
+ if(0 == max)
+ return 1; /* they are equal this far */
+
+ return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
+}
+
+/*
+ * Match a hostname against a wildcard pattern.
+ * E.g.
+ * "foo.host.com" matches "*.host.com".
+ *
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * http://tools.ietf.org/html/rfc6125#section-6.4.3
+ */
+
+static int hostmatch(const char *hostname, const char *pattern)
+{
+ const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
+ int wildcard_enabled;
+ size_t prefixlen, suffixlen;
+ pattern_wildcard = strchr(pattern, '*');
+ if(pattern_wildcard == NULL)
+ return Curl_raw_equal(pattern, hostname) ?
+ CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+
+ /* We require at least 2 dots in pattern to avoid too wide wildcard
+ match. */
+ wildcard_enabled = 1;
+ pattern_label_end = strchr(pattern, '.');
+ if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
+ pattern_wildcard > pattern_label_end ||
+ Curl_raw_nequal(pattern, "xn--", 4)) {
+ wildcard_enabled = 0;
+ }
+ if(!wildcard_enabled)
+ return Curl_raw_equal(pattern, hostname) ?
+ CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+
+ hostname_label_end = strchr(hostname, '.');
+ if(hostname_label_end == NULL ||
+ !Curl_raw_equal(pattern_label_end, hostname_label_end))
+ return CURL_HOST_NOMATCH;
+
+ /* The wildcard must match at least one character, so the left-most
+ label of the hostname is at least as large as the left-most label
+ of the pattern. */
+ if(hostname_label_end - hostname < pattern_label_end - pattern)
+ return CURL_HOST_NOMATCH;
+
+ prefixlen = pattern_wildcard - pattern;
+ suffixlen = pattern_label_end - (pattern_wildcard+1);
+ return Curl_raw_nequal(pattern, hostname, prefixlen) &&
+ Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
+ suffixlen) ?
+ CURL_HOST_MATCH : CURL_HOST_NOMATCH;
+}
+
+int Tool_Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
+{
+ if(!match_pattern || !*match_pattern ||
+ !hostname || !*hostname) /* sanity check */
+ return 0;
+
+ if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */
+ return 1;
+
+ if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH)
+ return 1;
+ return 0;
+}
diff --git a/modules/openssl/curl_hostcheck.h b/modules/openssl/curl_hostcheck.h
new file mode 100644
index 0000000000..1b7fbe81e3
--- /dev/null
+++ b/modules/openssl/curl_hostcheck.h
@@ -0,0 +1,39 @@
+#ifndef HEADER_TOOL_CURL_HOSTCHECK_H
+#define HEADER_TOOL_CURL_HOSTCHECK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#define CURL_HOST_NOMATCH 0
+#define CURL_HOST_MATCH 1
+int Tool_Curl_cert_hostcheck(const char *match_pattern, const char *hostname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HEADER_CURL_HOSTCHECK_H */
+
diff --git a/modules/openssl/register_types.cpp b/modules/openssl/register_types.cpp
new file mode 100644
index 0000000000..4aba9f530e
--- /dev/null
+++ b/modules/openssl/register_types.cpp
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "stream_peer_openssl.h"
+
+void register_openssl_types() {
+
+ ObjectTypeDB::register_type<StreamPeerOpenSSL>();
+ StreamPeerOpenSSL::initialize_ssl();
+}
+
+void unregister_openssl_types() {
+
+ StreamPeerOpenSSL::finalize_ssl();
+}
diff --git a/modules/openssl/register_types.h b/modules/openssl/register_types.h
new file mode 100644
index 0000000000..2db140cc80
--- /dev/null
+++ b/modules/openssl/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_openssl_types();
+void unregister_openssl_types();
diff --git a/modules/openssl/stream_peer_openssl.cpp b/modules/openssl/stream_peer_openssl.cpp
new file mode 100644
index 0000000000..b9bec4ca0b
--- /dev/null
+++ b/modules/openssl/stream_peer_openssl.cpp
@@ -0,0 +1,646 @@
+/*************************************************************************/
+/* stream_peer_openssl.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "stream_peer_openssl.h"
+//hostname matching code from curl
+
+
+//#include <openssl/applink.c> // To prevent crashing (see the OpenSSL FAQ)
+
+bool StreamPeerOpenSSL::_match_host_name(const char *name, const char *hostname) {
+
+ return Tool_Curl_cert_hostcheck(name,hostname)==CURL_HOST_MATCH;
+// print_line("MATCH: "+String(name)+" vs "+String(hostname));
+// return true;
+}
+
+Error StreamPeerOpenSSL::_match_common_name(const char *hostname, const X509 *server_cert) {
+
+ int common_name_loc = -1;
+ X509_NAME_ENTRY *common_name_entry = NULL;
+ ASN1_STRING *common_name_asn1 = NULL;
+ char *common_name_str = NULL;
+
+ // Find the position of the CN field in the Subject field of the certificate
+ common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1);
+
+ ERR_FAIL_COND_V(common_name_loc < 0, ERR_INVALID_PARAMETER );
+
+ // Extract the CN field
+ common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc);
+
+ ERR_FAIL_COND_V(common_name_entry == NULL, ERR_INVALID_PARAMETER );
+
+ // Convert the CN field to a C string
+ common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
+
+ ERR_FAIL_COND_V(common_name_asn1 == NULL, ERR_INVALID_PARAMETER );
+
+ common_name_str = (char *) ASN1_STRING_data(common_name_asn1);
+
+ // Make sure there isn't an embedded NUL character in the CN
+ bool malformed_certificate = (size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str);
+
+ ERR_FAIL_COND_V(malformed_certificate, ERR_INVALID_PARAMETER );
+
+
+ // Compare expected hostname with the CN
+
+
+ return _match_host_name(common_name_str,hostname)?OK:FAILED;
+
+}
+
+
+/**
+* Tries to find a match for hostname in the certificate's Subject Alternative Name extension.
+*
+*/
+
+Error StreamPeerOpenSSL::_match_subject_alternative_name(const char *hostname, const X509 *server_cert) {
+
+ Error result = FAILED;
+ int i;
+ int san_names_nb = -1;
+ STACK_OF(GENERAL_NAME) *san_names = NULL;
+
+ // Try to extract the names within the SAN extension from the certificate
+ san_names = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL);
+ if (san_names == NULL) {
+ return ERR_FILE_NOT_FOUND;
+ }
+ san_names_nb = sk_GENERAL_NAME_num(san_names);
+
+ // Check each name within the extension
+ for (i=0; i<san_names_nb; i++) {
+ const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
+
+ if (current_name->type == GEN_DNS) {
+ // Current name is a DNS name, let's check it
+ char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName);
+
+ // Make sure there isn't an embedded NUL character in the DNS name
+ if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
+ result = ERR_INVALID_PARAMETER;
+ break;
+ }
+ else { // Compare expected hostname with the DNS name
+ if (_match_host_name(dns_name, hostname)) {
+ result = OK;
+ break;
+ }
+ }
+ }
+ }
+ sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
+
+ return result;
+}
+
+/* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
+int StreamPeerOpenSSL::_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) {
+
+ /* This is the function that OpenSSL would call if we hadn't called
+ * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
+ * the default functionality, rather than replacing it. */
+
+ bool base_cert_valid = X509_verify_cert(x509_ctx);
+ if (!base_cert_valid) {
+ print_line("Cause: "+String(X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))));
+ ERR_print_errors_fp(stdout);
+ }
+ X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+
+ ERR_FAIL_COND_V(!server_cert,0);
+
+ char cert_str[256];
+ X509_NAME_oneline(X509_get_subject_name (server_cert),
+ cert_str, sizeof (cert_str));
+
+ print_line("CERT STR: "+String(cert_str));
+ print_line("VALID: "+itos(base_cert_valid));
+
+ if (!base_cert_valid)
+ return 0;
+
+ StreamPeerOpenSSL *ssl = (StreamPeerOpenSSL *)arg;
+
+ if (ssl->validate_hostname) {
+
+ Error err = _match_subject_alternative_name(ssl->hostname.utf8().get_data(),server_cert);
+
+ if (err==ERR_FILE_NOT_FOUND) {
+
+ err = _match_common_name(ssl->hostname.utf8().get_data(),server_cert);
+ }
+
+ if (err!=OK) {
+
+ ssl->status=STATUS_ERROR_HOSTNAME_MISMATCH;
+ return 0;
+ }
+ }
+
+ return 1;
+
+}
+
+
+
+int StreamPeerOpenSSL::_bio_create( BIO *b ) {
+ b->init = 1;
+ b->num = 0;
+ b->ptr = NULL;
+ b->flags = 0;
+ return 1;
+}
+
+int StreamPeerOpenSSL::_bio_destroy( BIO *b )
+{
+ if ( b == NULL )
+ return 0;
+
+ b->ptr = NULL; /* sb_tls_remove() will free it */
+ b->init = 0;
+ b->flags = 0;
+ return 1;
+}
+
+int StreamPeerOpenSSL::_bio_read( BIO *b, char *buf, int len ) {
+
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)b->ptr;
+
+ ERR_FAIL_COND_V( sp == NULL, 0);
+
+ BIO_clear_retry_flags( b );
+ if (sp->use_blocking) {
+
+ Error err = sp->base->get_data((uint8_t*)buf,len);
+ if (err!=OK) {
+ return -1;
+ }
+
+ return len;
+ } else {
+
+ int got;
+ Error err = sp->base->get_partial_data((uint8_t*)buf,len,got);
+ if (err!=OK) {
+ return -1;
+ }
+ if (got==0) {
+ BIO_set_retry_read( b );
+ }
+ return got;
+ }
+
+ //unreachable
+ return 0;
+}
+
+int StreamPeerOpenSSL::_bio_write( BIO *b, const char *buf, int len ) {
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ StreamPeerOpenSSL *sp = (StreamPeerOpenSSL *)b->ptr;
+
+ ERR_FAIL_COND_V( sp == NULL, 0);
+
+ BIO_clear_retry_flags( b );
+ if (sp->use_blocking) {
+
+ Error err = sp->base->put_data((const uint8_t*)buf,len);
+ if (err!=OK) {
+ return -1;
+ }
+
+ return len;
+ } else {
+
+ int sent;
+ Error err = sp->base->put_partial_data((const uint8_t*)buf,len,sent);
+ if (err!=OK) {
+ return -1;
+ }
+ if (sent==0) {
+ BIO_set_retry_write( b );
+ }
+ return sent;
+
+ }
+
+ //unreachable
+ return 0;
+}
+
+long StreamPeerOpenSSL::_bio_ctrl( BIO *b, int cmd, long num, void *ptr )
+{
+ if ( cmd == BIO_CTRL_FLUSH ) {
+ /* The OpenSSL library needs this */
+ return 1;
+ }
+ return 0;
+}
+
+int StreamPeerOpenSSL::_bio_gets( BIO *b, char *buf, int len )
+{
+ return -1;
+}
+
+int StreamPeerOpenSSL::_bio_puts( BIO *b, const char *str )
+{
+ return _bio_write( b, str, strlen( str ) );
+}
+
+BIO_METHOD StreamPeerOpenSSL::_bio_method = {
+ /* it's a source/sink BIO */
+ ( 100 | 0x400 ),
+ "streampeer glue",
+ _bio_write,
+ _bio_read,
+ _bio_puts,
+ _bio_gets,
+ _bio_ctrl,
+ _bio_create,
+ _bio_destroy
+};
+
+Error StreamPeerOpenSSL::connect(Ref<StreamPeer> p_base, bool p_validate_certs, const String& p_for_hostname) {
+
+ if (connected)
+ disconnect();
+
+
+ hostname=p_for_hostname;
+ status=STATUS_DISCONNECTED;
+
+ // Set up a SSL_CTX object, which will tell our BIO object how to do its work
+ ctx = SSL_CTX_new(SSLv23_client_method());
+ base=p_base;
+ validate_certs=p_validate_certs;
+ validate_hostname=p_for_hostname!="";
+
+
+
+
+ if (p_validate_certs) {
+
+
+ if (certs.size()) {
+ //yay for undocumented OpenSSL functions
+
+ X509_STORE *store = SSL_CTX_get_cert_store(ctx);
+ for(int i=0;i<certs.size();i++) {
+
+ X509_STORE_add_cert(store,certs[i]);
+
+ }
+#if 0
+ const unsigned char *in=(const unsigned char *)certs.ptr();
+ X509 *Cert = d2i_X509(NULL, &in, certs.size()-1);
+ if (!Cert) {
+ print_line(String(ERR_error_string(ERR_get_error(),NULL)));
+ }
+ ERR_FAIL_COND_V(!Cert,ERR_PARSE_ERROR);
+
+ X509_STORE *store = SSL_CTX_get_cert_store(ctx);
+ X509_STORE_add_cert(store,Cert);
+
+ //char *str = X509_NAME_oneline(X509_get_subject_name(Cert),0,0);
+ //printf ("subject: %s\n", str); /* [1] */
+#endif
+ }
+
+ //used for testing
+ //int res = SSL_CTX_load_verify_locations(ctx,"/etc/ssl/certs/ca-certificates.crt",NULL);
+ //print_line("verify locations res: "+itos(res));
+
+
+ /* Ask OpenSSL to verify the server certificate. Note that this
+ * does NOT include verifying that the hostname is correct.
+ * So, by itself, this means anyone with any legitimate
+ * CA-issued certificate for any website, can impersonate any
+ * other website in the world. This is not good. See "The
+ * Most Dangerous Code in the World" article at
+ * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
+ */
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+ /* This is how we solve the problem mentioned in the previous
+ * comment. We "wrap" OpenSSL's validation routine in our
+ * own routine, which also validates the hostname by calling
+ * the code provided by iSECPartners. Note that even though
+ * the "Everything You've Always Wanted to Know About
+ * Certificate Validation With OpenSSL (But Were Afraid to
+ * Ask)" paper from iSECPartners says very explicitly not to
+ * call SSL_CTX_set_cert_verify_callback (at the bottom of
+ * page 2), what we're doing here is safe because our
+ * cert_verify_callback() calls X509_verify_cert(), which is
+ * OpenSSL's built-in routine which would have been called if
+ * we hadn't set the callback. Therefore, we're just
+ * "wrapping" OpenSSL's routine, not replacing it. */
+ SSL_CTX_set_cert_verify_callback (ctx, _cert_verify_callback,this);
+
+ //Let the verify_callback catch the verify_depth error so that we get an appropriate error in the logfile. (??)
+ SSL_CTX_set_verify_depth(ctx,max_cert_chain_depth + 1);
+
+ }
+
+
+
+
+
+ ssl = SSL_new( ctx );
+ bio = BIO_new( &_bio_method );
+ bio->ptr = this;
+ SSL_set_bio( ssl, bio, bio );
+
+ if (p_for_hostname!=String()) {
+ SSL_set_tlsext_host_name(ssl,p_for_hostname.utf8().get_data());
+ }
+
+ use_blocking=true; // let handshake use blocking
+ // Set the SSL to automatically retry on failure.
+ SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
+
+ // Same as before, try to connect.
+ int result = SSL_connect( ssl );
+
+ print_line("CONNECTION RESULT: "+itos(result));
+ if (result<1) {
+ ERR_print_errors_fp(stdout);
+ _print_error(result);
+ }
+
+ X509 * peer = SSL_get_peer_certificate(ssl);
+
+ if (peer) {
+ bool cert_ok = SSL_get_verify_result(ssl) == X509_V_OK;
+ print_line("cert_ok: "+itos(cert_ok));
+
+ } else if (validate_certs){
+ status=STATUS_ERROR_NO_CERTIFICATE;
+ }
+
+ connected=true;
+ status=STATUS_CONNECTED;
+
+ return OK;
+}
+
+Error StreamPeerOpenSSL::accept(Ref<StreamPeer> p_base) {
+
+
+ return ERR_UNAVAILABLE;
+}
+
+void StreamPeerOpenSSL::_print_error(int err) {
+
+ err = SSL_get_error(ssl,err);
+ switch(err) {
+ case SSL_ERROR_NONE: ERR_PRINT("NO ERROR: The TLS/SSL I/O operation completed"); break;
+ case SSL_ERROR_ZERO_RETURN: ERR_PRINT("The TLS/SSL connection has been closed.");
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ ERR_PRINT("The operation did not complete."); break;
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_ACCEPT:
+ ERR_PRINT("The connect/accept operation did not complete"); break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ ERR_PRINT("The operation did not complete because an application callback set by SSL_CTX_set_client_cert_cb() has asked to be called again."); break;
+ case SSL_ERROR_SYSCALL:
+ ERR_PRINT("Some I/O error occurred. The OpenSSL error queue may contain more information on the error."); break;
+ case SSL_ERROR_SSL:
+ ERR_PRINT("A failure in the SSL library occurred, usually a protocol error."); break;
+
+ }
+}
+
+Error StreamPeerOpenSSL::put_data(const uint8_t* p_data,int p_bytes) {
+
+ ERR_FAIL_COND_V(!connected,ERR_UNCONFIGURED);
+
+ while(p_bytes>0) {
+ int ret = SSL_write(ssl,p_data,p_bytes);
+ if (ret<=0) {
+ _print_error(ret);
+ disconnect();
+ return ERR_CONNECTION_ERROR;
+ }
+ p_data+=ret;
+ p_bytes-=ret;
+ }
+
+ return OK;
+
+}
+
+Error StreamPeerOpenSSL::put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent){
+
+ ERR_FAIL_COND_V(!connected,ERR_UNCONFIGURED);
+ if (p_bytes==0)
+ return OK;
+
+ Error err = put_data(p_data,p_bytes);
+ if (err!=OK)
+ return err;
+
+ r_sent=p_bytes;
+ return OK;
+
+}
+
+Error StreamPeerOpenSSL::get_data(uint8_t* p_buffer, int p_bytes){
+
+ ERR_FAIL_COND_V(!connected,ERR_UNCONFIGURED);
+
+ while(p_bytes>0) {
+
+ int ret = SSL_read(ssl,p_buffer,p_bytes);
+ if (ret<=0) {
+ _print_error(ret);
+ disconnect();
+ return ERR_CONNECTION_ERROR;
+ }
+ p_buffer+=ret;
+ p_bytes-=ret;
+ }
+
+ return OK;
+}
+
+Error StreamPeerOpenSSL::get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received){
+
+ ERR_FAIL_COND_V(!connected,ERR_UNCONFIGURED);
+ if (p_bytes==0) {
+ r_received=0;
+ return OK;
+ }
+
+ Error err = get_data(p_buffer,p_bytes);
+ if (err!=OK)
+ return err;
+ r_received=p_bytes;
+ return OK;
+}
+
+int StreamPeerOpenSSL::get_available_bytes() const {
+
+ ERR_FAIL_COND_V(!connected,0);
+
+ return SSL_pending(ssl);
+
+}
+StreamPeerOpenSSL::StreamPeerOpenSSL() {
+
+ ctx=NULL;
+ ssl=NULL;
+ bio=NULL;
+ connected=false;
+ use_blocking=true; //might be improved int the future, but for now it always blocks
+ max_cert_chain_depth=9;
+ flags=0;
+}
+
+void StreamPeerOpenSSL::disconnect() {
+
+ if (!connected)
+ return;
+ SSL_shutdown( ssl );
+ SSL_free( ssl );
+ SSL_CTX_free(ctx);
+ base=Ref<StreamPeer>();
+ connected=false;
+ validate_certs=false;
+ validate_hostname=false;
+ status=STATUS_DISCONNECTED;
+
+
+}
+
+StreamPeerOpenSSL::Status StreamPeerOpenSSL::get_status() const {
+
+ return status;
+}
+
+
+StreamPeerOpenSSL::~StreamPeerOpenSSL() {
+ disconnect();
+}
+
+StreamPeerSSL* StreamPeerOpenSSL::_create_func() {
+
+ return memnew( StreamPeerOpenSSL );
+}
+
+
+Vector<X509*> StreamPeerOpenSSL::certs;
+
+
+void StreamPeerOpenSSL::_load_certs(const ByteArray& p_array) {
+
+ ByteArray::Read r = p_array.read();
+ BIO* mem = BIO_new(BIO_s_mem());
+ BIO_puts(mem,(const char*)r.ptr());
+ while(true) {
+ X509*cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
+ if (!cert)
+ break;
+ certs.push_back(cert);
+ }
+ BIO_free(mem);
+}
+
+void StreamPeerOpenSSL::initialize_ssl() {
+
+ available=true;
+
+ load_certs_func=_load_certs;
+
+ _create=_create_func;
+ CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
+ SSL_library_init(); // Initialize OpenSSL's SSL libraries
+ SSL_load_error_strings(); // Load SSL error strings
+ ERR_load_BIO_strings(); // Load BIO error strings
+ OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
+ String certs_path =GLOBAL_DEF("ssl/certificates","");
+ Globals::get_singleton()->set_custom_property_info("ssl/certificates",PropertyInfo(Variant::STRING,"ssl/certificates",PROPERTY_HINT_FILE,"*.crt"));
+ if (certs_path!="") {
+
+
+
+ FileAccess *f=FileAccess::open(certs_path,FileAccess::READ);
+ if (f) {
+ ByteArray arr;
+ int flen = f->get_len();
+ arr.resize(flen+1);
+ {
+ ByteArray::Write w = arr.write();
+ f->get_buffer(w.ptr(),flen);
+ w[flen]=0; //end f string
+ }
+
+ memdelete(f);
+
+ _load_certs(arr);
+ print_line("Loaded certs from '"+certs_path+"': "+itos(certs.size()));
+ }
+ }
+ String config_path =GLOBAL_DEF("ssl/config","");
+ Globals::get_singleton()->set_custom_property_info("ssl/config",PropertyInfo(Variant::STRING,"ssl/config",PROPERTY_HINT_FILE,"*.cnf"));
+ if (config_path!="") {
+
+ Vector<uint8_t> data = FileAccess::get_file_as_array(config_path);
+ if (data.size()) {
+ data.push_back(0);
+ BIO* mem = BIO_new(BIO_s_mem());
+ BIO_puts(mem,(const char*) data.ptr());
+
+ while(true) {
+ X509*cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
+ if (!cert)
+ break;
+ certs.push_back(cert);
+ }
+ BIO_free(mem);
+ }
+ print_line("Loaded certs from '"+certs_path+"': "+itos(certs.size()));
+
+ }
+
+}
+
+void StreamPeerOpenSSL::finalize_ssl(){
+
+ for(int i=0;i<certs.size();i++) {
+ X509_free(certs[i]);
+ }
+ certs.clear();
+}
diff --git a/modules/openssl/stream_peer_openssl.h b/modules/openssl/stream_peer_openssl.h
new file mode 100644
index 0000000000..853ede2036
--- /dev/null
+++ b/modules/openssl/stream_peer_openssl.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* stream_peer_openssl.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef STREAM_PEER_OPEN_SSL_H
+#define STREAM_PEER_OPEN_SSL_H
+
+#include <stdio.h> // If you don't know what this is for stop reading now.
+#include "io/stream_peer_ssl.h"
+#include "globals.h"
+#include "os/file_access.h"
+#include "curl_hostcheck.h"
+
+#include <openssl/bio.h> // BIO objects for I/O
+#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
+#include <openssl/err.h> // Error reporting
+#include <openssl/x509v3.h>
+
+class StreamPeerOpenSSL : public StreamPeerSSL {
+private:
+ static int _bio_create( BIO *b );
+ static int _bio_destroy( BIO *b );
+ static int _bio_read( BIO *b, char *buf, int len );
+ static int _bio_write( BIO *b, const char *buf, int len );
+ static long _bio_ctrl( BIO *b, int cmd, long num, void *ptr );
+ static int _bio_gets( BIO *b, char *buf, int len );
+ static int _bio_puts( BIO *b, const char *str );
+
+ static BIO_METHOD _bio_method;
+
+ static bool _match_host_name(const char *name, const char *hostname);
+ static Error _match_common_name(const char *hostname, const X509 *server_cert);
+ static Error _match_subject_alternative_name(const char *hostname, const X509 *server_cert);
+
+
+ static int _cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg);
+
+
+ Status status;
+ String hostname;
+ int max_cert_chain_depth;
+ SSL_CTX* ctx;
+ SSL* ssl;
+ BIO* bio;
+ bool connected;
+ int flags;
+ bool use_blocking;
+ bool validate_certs;
+ bool validate_hostname;
+
+ Ref<StreamPeer> base;
+
+ static StreamPeerSSL* _create_func();
+ void _print_error(int err);
+
+ static Vector<X509*> certs;
+
+ static void _load_certs(const ByteArray& p_array);
+protected:
+ static void _bind_methods();
+public:
+
+
+ virtual Error accept(Ref<StreamPeer> p_base);
+ virtual Error connect(Ref<StreamPeer> p_base,bool p_validate_certs=false,const String& p_for_hostname=String());
+ virtual Status get_status() const;
+
+ virtual void disconnect();
+
+ virtual Error put_data(const uint8_t* p_data,int p_bytes);
+ virtual Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent);
+
+ virtual Error get_data(uint8_t* p_buffer, int p_bytes);
+ virtual Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received);
+
+ virtual int get_available_bytes() const;
+
+ static void initialize_ssl();
+ static void finalize_ssl();
+
+ StreamPeerOpenSSL();
+ ~StreamPeerOpenSSL();
+};
+
+#endif // STREAM_PEER_SSL_H
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
new file mode 100644
index 0000000000..603f5a48c4
--- /dev/null
+++ b/modules/opus/SCsub
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_opus = env_modules.Clone()
+
+# Thirdparty source files
+if (env["opus"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/opus/"
+
+ thirdparty_sources = [
+ "silk/tables_other.c",
+ "silk/sum_sqr_shift.c",
+ "silk/PLC.c",
+ "silk/dec_API.c",
+ "silk/decode_pulses.c",
+ "silk/inner_prod_aligned.c",
+ "silk/init_encoder.c",
+ "silk/interpolate.c",
+ "silk/stereo_encode_pred.c",
+ "silk/decode_frame.c",
+ "silk/NLSF_del_dec_quant.c",
+ "silk/VAD.c",
+ "silk/resampler_private_AR2.c",
+ "silk/NLSF_unpack.c",
+ "silk/resampler_down2.c",
+ "silk/sort.c",
+ "silk/resampler_private_IIR_FIR.c",
+ "silk/resampler_down2_3.c",
+ "silk/resampler_private_up2_HQ.c",
+ "silk/tables_gain.c",
+ "silk/stereo_find_predictor.c",
+ "silk/stereo_quant_pred.c",
+ "silk/NLSF_stabilize.c",
+ "silk/ana_filt_bank_1.c",
+ "silk/check_control_input.c",
+ "silk/bwexpander.c",
+ "silk/A2NLSF.c",
+ "silk/LPC_inv_pred_gain.c",
+ "silk/log2lin.c",
+ "silk/process_NLSFs.c",
+ "silk/sigm_Q15.c",
+ "silk/VQ_WMat_EC.c",
+ "silk/quant_LTP_gains.c",
+ "silk/resampler_private_down_FIR.c",
+ "silk/NLSF_decode.c",
+ "silk/control_codec.c",
+ "silk/NLSF_VQ_weights_laroia.c",
+ "silk/decode_pitch.c",
+ "silk/stereo_decode_pred.c",
+ "silk/tables_pulses_per_block.c",
+ "silk/init_decoder.c",
+ "silk/table_LSF_cos.c",
+ "silk/decode_core.c",
+ "silk/code_signs.c",
+ "silk/enc_API.c",
+ "silk/tables_LTP.c",
+ "silk/pitch_est_tables.c",
+ "silk/biquad_alt.c",
+ "silk/encode_indices.c",
+ "silk/tables_NLSF_CB_WB.c",
+ "silk/debug.c",
+ "silk/decode_parameters.c",
+ "silk/tables_pitch_lag.c",
+ "silk/NLSF2A.c",
+ "silk/resampler.c",
+ "silk/decode_indices.c",
+ "silk/NLSF_VQ.c",
+ "silk/bwexpander_32.c",
+ "silk/tables_NLSF_CB_NB_MB.c",
+ "silk/encode_pulses.c",
+ "silk/NSQ_del_dec.c",
+ "silk/control_SNR.c",
+ "silk/shell_coder.c",
+ "silk/NLSF_encode.c",
+ "silk/stereo_MS_to_LR.c",
+ "silk/stereo_LR_to_MS.c",
+ "silk/HP_variable_cutoff.c",
+ "silk/LPC_analysis_filter.c",
+ "silk/CNG.c",
+ "silk/decoder_set_fs.c",
+ "silk/resampler_rom.c",
+ "silk/control_audio_bandwidth.c",
+ "silk/lin2log.c",
+ "silk/LP_variable_cutoff.c",
+ "silk/NSQ.c",
+ "silk/gain_quant.c",
+ "celt/laplace.c",
+ "celt/vq.c",
+ "celt/quant_bands.c",
+ "celt/kiss_fft.c",
+ "celt/entcode.c",
+ "celt/entenc.c",
+ "celt/celt_lpc.c",
+ "celt/pitch.c",
+ "celt/rate.c",
+ "celt/mathops.c",
+ #"celt/arm/armcpu.c",
+ #"celt/arm/celt_neon_intr.c",
+ #"celt/arm/celt_ne10_mdct.c",
+ #"celt/arm/celt_ne10_fft.c",
+ #"celt/arm/arm_celt_map.c",
+ "celt/celt_encoder.c",
+ "celt/celt.c",
+ "celt/bands.c",
+ "celt/cwrs.c",
+ "celt/entdec.c",
+ "celt/celt_decoder.c",
+ "celt/mdct.c",
+ "celt/modes.c",
+ "repacketizer.c",
+ "mlp_data.c",
+ "opus_multistream.c",
+ "opusfile.c",
+ "opus_encoder.c",
+ "analysis.c",
+ "mlp.c",
+ "info.c",
+ "stream.c",
+ "opus_decoder.c",
+ "internal.c",
+ "wincerts.c",
+ "opus.c",
+ "opus_multistream_encoder.c",
+ "http.c",
+ "opus_multistream_decoder.c"
+ ]
+
+ opus_sources_silk = []
+
+ if("opus_fixed_point" in env and env.opus_fixed_point=="yes"):
+ env_opus.Append(CFLAGS = ["-DFIXED_POINT"])
+ opus_sources_silk = [
+ "silk/fixed/schur64_FIX.c",
+ "silk/fixed/residual_energy16_FIX.c",
+ "silk/fixed/encode_frame_FIX.c",
+ "silk/fixed/regularize_correlations_FIX.c",
+ "silk/fixed/apply_sine_window_FIX.c",
+ "silk/fixed/solve_LS_FIX.c",
+ "silk/fixed/schur_FIX.c",
+ "silk/fixed/pitch_analysis_core_FIX.c",
+ "silk/fixed/noise_shape_analysis_FIX.c",
+ "silk/fixed/find_LTP_FIX.c",
+ "silk/fixed/vector_ops_FIX.c",
+ "silk/fixed/autocorr_FIX.c",
+ "silk/fixed/warped_autocorrelation_FIX.c",
+ "silk/fixed/find_pitch_lags_FIX.c",
+ "silk/fixed/k2a_Q16_FIX.c",
+ "silk/fixed/LTP_scale_ctrl_FIX.c",
+ "silk/fixed/corrMatrix_FIX.c",
+ "silk/fixed/prefilter_FIX.c",
+ "silk/fixed/find_LPC_FIX.c",
+ "silk/fixed/residual_energy_FIX.c",
+ "silk/fixed/process_gains_FIX.c",
+ "silk/fixed/LTP_analysis_filter_FIX.c",
+ "silk/fixed/k2a_FIX.c",
+ "silk/fixed/burg_modified_FIX.c",
+ "silk/fixed/find_pred_coefs_FIX.c"
+ ]
+ else:
+ opus_sources_silk = [
+ "silk/float/LTP_scale_ctrl_FLP.c",
+ "silk/float/regularize_correlations_FLP.c",
+ "silk/float/corrMatrix_FLP.c",
+ "silk/float/LPC_analysis_filter_FLP.c",
+ "silk/float/levinsondurbin_FLP.c",
+ "silk/float/schur_FLP.c",
+ "silk/float/scale_vector_FLP.c",
+ "silk/float/apply_sine_window_FLP.c",
+ "silk/float/pitch_analysis_core_FLP.c",
+ "silk/float/wrappers_FLP.c",
+ "silk/float/bwexpander_FLP.c",
+ "silk/float/warped_autocorrelation_FLP.c",
+ "silk/float/solve_LS_FLP.c",
+ "silk/float/find_LPC_FLP.c",
+ "silk/float/autocorrelation_FLP.c",
+ "silk/float/find_pred_coefs_FLP.c",
+ "silk/float/find_pitch_lags_FLP.c",
+ "silk/float/burg_modified_FLP.c",
+ "silk/float/find_LTP_FLP.c",
+ "silk/float/energy_FLP.c",
+ "silk/float/sort_FLP.c",
+ "silk/float/LPC_inv_pred_gain_FLP.c",
+ "silk/float/k2a_FLP.c",
+ "silk/float/noise_shape_analysis_FLP.c",
+ "silk/float/inner_product_FLP.c",
+ "silk/float/process_gains_FLP.c",
+ "silk/float/encode_frame_FLP.c",
+ "silk/float/scale_copy_vector_FLP.c",
+ "silk/float/residual_energy_FLP.c",
+ "silk/float/LTP_analysis_filter_FLP.c",
+ "silk/float/prefilter_FLP.c"
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk]
+
+ env_opus.add_source_files(env.modules_sources, thirdparty_sources)
+ env_opus.Append(CFLAGS=["-DHAVE_CONFIG_H"])
+
+ thirdparty_include_paths = [
+ "",
+ "celt",
+ "silk",
+ "silk/fixed",
+ "silk/float",
+ ]
+ env_opus.Append(CPPPATH = [thirdparty_dir + "/" + dir for dir in thirdparty_include_paths])
+
+ # also requires libogg
+ if (env["libogg"] != "system"): # builtin
+ env_opus.Append(CPPPATH = ["#thirdparty/libogg"])
+
+# Module files
+env_opus.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/opus/audio_stream_opus.cpp b/modules/opus/audio_stream_opus.cpp
new file mode 100644
index 0000000000..ab53fee0c9
--- /dev/null
+++ b/modules/opus/audio_stream_opus.cpp
@@ -0,0 +1,376 @@
+/*************************************************************************/
+/* audio_stream_opus.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: George Marques <george@gmarqu.es> */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_stream_opus.h"
+
+const float AudioStreamPlaybackOpus::osrate=48000.0f;
+
+int AudioStreamPlaybackOpus::_op_read_func(void *_stream, unsigned char *_ptr, int _nbytes) {
+ FileAccess *fa=(FileAccess*)_stream;
+
+ if(fa->eof_reached())
+ return 0;
+
+ uint8_t *dst = (uint8_t*)_ptr;
+
+ int read = fa->get_buffer(dst, _nbytes);
+
+ return read;
+}
+
+int AudioStreamPlaybackOpus::_op_seek_func(void *_stream, opus_int64 _offset, int _whence){
+
+#ifdef SEEK_SET
+ FileAccess *fa=(FileAccess*)_stream;
+
+ switch (_whence) {
+ case SEEK_SET: {
+ fa->seek(_offset);
+ } break;
+ case SEEK_CUR: {
+ fa->seek(fa->get_pos()+_offset);
+ } break;
+ case SEEK_END: {
+ fa->seek_end(_offset);
+ } break;
+ default: {
+ ERR_PRINT("BUG, wtf was whence set to?\n");
+ }
+ }
+ int ret=fa->eof_reached()?-1:0;
+ return ret;
+#else
+ return -1; // no seeking
+#endif
+}
+
+int AudioStreamPlaybackOpus::_op_close_func(void *_stream) {
+ if (!_stream)
+ return 0;
+ FileAccess *fa=(FileAccess*)_stream;
+ if (fa->is_open())
+ fa->close();
+ return 0;
+}
+
+opus_int64 AudioStreamPlaybackOpus::_op_tell_func(void *_stream) {
+ FileAccess *_fa = (FileAccess*)_stream;
+ return (opus_int64)_fa->get_pos();
+}
+
+void AudioStreamPlaybackOpus::_clear_stream() {
+ if(!stream_loaded)
+ return;
+
+ op_free(opus_file);
+ _close_file();
+
+ stream_loaded=false;
+ stream_channels=1;
+ playing=false;
+}
+
+void AudioStreamPlaybackOpus::_close_file() {
+ if (f) {
+ memdelete(f);
+ f=NULL;
+ }
+}
+
+Error AudioStreamPlaybackOpus::_load_stream() {
+
+ ERR_FAIL_COND_V(!stream_valid,ERR_UNCONFIGURED);
+
+ _clear_stream();
+ if (file=="")
+ return ERR_INVALID_DATA;
+
+ Error err;
+ f=FileAccess::open(file,FileAccess::READ,&err);
+
+ if (err) {
+ ERR_FAIL_COND_V( err, err );
+ }
+
+ int _err = 0;
+
+ opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&_err);
+
+ switch (_err) {
+ case OP_EREAD: { // - Can't read the file.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CANT_READ );
+ } break;
+ case OP_EVERSION: // - Unrecognized version number.
+ case OP_ENOTFORMAT: // - Stream is not Opus data.
+ case OP_EIMPL : { // - Stream used non-implemented feature.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ } break;
+ case OP_EBADLINK: // - Failed to find old data after seeking.
+ case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks.
+ case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CORRUPT );
+ } break;
+ case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_BUG );
+ } break;
+ }
+ repeats=0;
+ stream_loaded=true;
+
+
+ return OK;
+}
+
+AudioStreamPlaybackOpus::AudioStreamPlaybackOpus() {
+ loops=false;
+ playing=false;
+ f = NULL;
+ stream_loaded=false;
+ stream_valid=false;
+ repeats=0;
+ paused=true;
+ stream_channels=0;
+ current_section=0;
+ length=0;
+ loop_restart_time=0;
+ pre_skip=0;
+
+ _op_callbacks.read = _op_read_func;
+ _op_callbacks.seek = _op_seek_func;
+ _op_callbacks.tell = _op_tell_func;
+ _op_callbacks.close = _op_close_func;
+}
+
+Error AudioStreamPlaybackOpus::set_file(const String &p_file) {
+ file=p_file;
+ stream_valid=false;
+ Error err;
+ f=FileAccess::open(file,FileAccess::READ,&err);
+
+ if (err) {
+ ERR_FAIL_COND_V( err, err );
+ }
+
+ int _err;
+
+ opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&_err);
+
+ switch (_err) {
+ case OP_EREAD: { // - Can't read the file.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CANT_READ );
+ } break;
+ case OP_EVERSION: // - Unrecognized version number.
+ case OP_ENOTFORMAT: // - Stream is not Opus data.
+ case OP_EIMPL : { // - Stream used non-implemented feature.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ } break;
+ case OP_EBADLINK: // - Failed to find old data after seeking.
+ case OP_EBADTIMESTAMP: // - Timestamp failed the validity checks.
+ case OP_EBADHEADER: { // - Invalid or mising Opus bitstream header.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CORRUPT );
+ } break;
+ case OP_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_BUG );
+ } break;
+ }
+
+ const OpusHead *oinfo = op_head(opus_file,-1);
+
+ stream_channels=oinfo->channel_count;
+ pre_skip=oinfo->pre_skip;
+ frames_mixed=pre_skip;
+ ogg_int64_t len = op_pcm_total(opus_file,-1);
+ if(len < 0) {
+ length = 0;
+ } else {
+ length=(len/osrate);
+ }
+
+ op_free(opus_file);
+ memdelete(f);
+ f=NULL;
+ stream_valid=true;
+
+
+ return OK;
+}
+
+void AudioStreamPlaybackOpus::play(float p_from) {
+ if (playing)
+ stop();
+
+ if (_load_stream()!=OK)
+ return;
+
+ frames_mixed=pre_skip;
+ playing=true;
+ if (p_from>0) {
+ seek_pos(p_from);
+ }
+}
+
+void AudioStreamPlaybackOpus::stop() {
+ _clear_stream();
+ playing=false;
+}
+
+void AudioStreamPlaybackOpus::seek_pos(float p_time) {
+ if(!playing) return;
+ ogg_int64_t pcm_offset = (ogg_int64_t)(p_time * osrate);
+ bool ok = op_pcm_seek(opus_file,pcm_offset)==0;
+ if(!ok) {
+ ERR_PRINT("Seek time over stream size.");
+ return;
+ }
+ frames_mixed=osrate*p_time;
+}
+
+int AudioStreamPlaybackOpus::mix(int16_t* p_bufer,int p_frames) {
+ if (!playing)
+ return 0;
+
+ int total=p_frames;
+
+ while (true) {
+
+ int todo = p_frames;
+
+ if (todo==0 || todo<MIN_MIX) {
+ break;
+ }
+
+ int ret=op_read(opus_file,(opus_int16*)p_bufer,todo*stream_channels,&current_section);
+ if (ret<0) {
+ playing = false;
+ ERR_EXPLAIN("Error reading Opus File: "+file);
+ ERR_BREAK(ret<0);
+ } else if (ret==0) { // end of song, reload?
+ op_free(opus_file);
+
+ _close_file();
+
+ f=FileAccess::open(file,FileAccess::READ);
+
+ int errv = 0;
+ opus_file = op_open_callbacks(f,&_op_callbacks,NULL,0,&errv);
+ if (errv!=0) {
+ playing=false;
+ break; // :(
+ }
+
+ if (!has_loop()) {
+ playing=false;
+ repeats=1;
+ break;
+ }
+
+ if (loop_restart_time) {
+ bool ok = op_pcm_seek(opus_file, (loop_restart_time*osrate)+pre_skip)==0;
+ if (!ok) {
+ playing=false;
+ ERR_PRINT("loop restart time rejected")
+ }
+
+ frames_mixed=(loop_restart_time*osrate)+pre_skip;
+ } else {
+ frames_mixed=pre_skip;
+ }
+ repeats++;
+ continue;
+
+ }
+
+ stream_channels=op_head(opus_file,current_section)->channel_count;
+
+ frames_mixed+=ret;
+
+ p_bufer+=ret*stream_channels;
+ p_frames-=ret;
+
+ }
+
+ return total-p_frames;
+}
+
+float AudioStreamPlaybackOpus::get_length() const {
+ if(!stream_loaded) {
+ if(const_cast<AudioStreamPlaybackOpus*>(this)->_load_stream() != OK)
+ return 0;
+ }
+ return length;
+}
+
+float AudioStreamPlaybackOpus::get_pos() const {
+
+ int32_t frames = int32_t(frames_mixed);
+ if (frames < 0)
+ frames=0;
+ return double(frames) / osrate;
+}
+
+int AudioStreamPlaybackOpus::get_minimum_buffer_size() const {
+ return MIN_MIX;
+}
+
+AudioStreamPlaybackOpus::~AudioStreamPlaybackOpus() {
+ _clear_stream();
+}
+
+RES ResourceFormatLoaderAudioStreamOpus::load(const String &p_path, const String& p_original_path, Error *r_error) {
+ if (r_error)
+ *r_error=OK;
+
+ AudioStreamOpus *opus_stream = memnew(AudioStreamOpus);
+ opus_stream->set_file(p_path);
+ return Ref<AudioStreamOpus>(opus_stream);
+}
+
+void ResourceFormatLoaderAudioStreamOpus::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("opus");
+}
+String ResourceFormatLoaderAudioStreamOpus::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="opus")
+ return "AudioStreamOpus";
+ return "";
+}
+
+bool ResourceFormatLoaderAudioStreamOpus::handles_type(const String& p_type) const {
+ return (p_type=="AudioStream" || p_type=="AudioStreamOpus");
+}
diff --git a/modules/opus/audio_stream_opus.h b/modules/opus/audio_stream_opus.h
new file mode 100644
index 0000000000..4da66fe167
--- /dev/null
+++ b/modules/opus/audio_stream_opus.h
@@ -0,0 +1,141 @@
+/*************************************************************************/
+/* audio_stream_opus.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Author: George Marques <george@gmarqu.es> */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef AUDIO_STREAM_OPUS_H
+#define AUDIO_STREAM_OPUS_H
+
+#include "io/resource_loader.h"
+#include "os/file_access.h"
+#include "scene/resources/audio_stream.h"
+
+#include <opusfile.h>
+
+class AudioStreamPlaybackOpus : public AudioStreamPlayback {
+
+ OBJ_TYPE(AudioStreamPlaybackOpus,AudioStreamPlayback)
+
+ enum {
+ MIN_MIX=1024
+ };
+
+ FileAccess *f;
+
+ OpusFileCallbacks _op_callbacks;
+ float length;
+ static int _op_read_func(void *_stream, unsigned char *_ptr, int _nbytes);
+ static int _op_seek_func(void *_stream, opus_int64 _offset, int _whence);
+ static int _op_close_func(void *_stream);
+ static opus_int64 _op_tell_func(void *_stream);
+ static const float osrate;
+
+ String file;
+ int64_t frames_mixed;
+
+ bool stream_loaded;
+ volatile bool playing;
+ OggOpusFile *opus_file;
+ int stream_channels;
+ int current_section;
+ int pre_skip;
+
+ bool paused;
+ bool loops;
+ int repeats;
+
+ Error _load_stream();
+ void _clear_stream();
+ void _close_file();
+
+ bool stream_valid;
+ float loop_restart_time;
+
+public:
+ Error set_file(const String& p_file);
+
+ virtual void play(float p_from=0);
+ virtual void stop();
+ virtual bool is_playing() const { return playing; }
+
+ virtual void set_loop_restart_time(float p_time) { loop_restart_time=p_time; }
+
+ virtual void set_paused(bool p_paused) { paused=p_paused; }
+ virtual bool is_paused() const { return paused; }
+
+ virtual void set_loop(bool p_enable) { loops=p_enable; }
+ virtual bool has_loop() const {return loops; }
+
+ virtual float get_length() const;
+
+ virtual String get_stream_name() const { return ""; }
+
+ virtual int get_loop_count() const { return repeats; }
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+ virtual int get_channels() const { return stream_channels; }
+ virtual int get_mix_rate() const { return osrate; }
+
+ virtual int get_minimum_buffer_size() const;
+
+ virtual int mix(int16_t* p_bufer,int p_frames);
+
+ AudioStreamPlaybackOpus();
+ ~AudioStreamPlaybackOpus();
+};
+
+
+class AudioStreamOpus: public AudioStream {
+
+ OBJ_TYPE(AudioStreamOpus,AudioStream)
+
+ String file;
+public:
+
+ Ref<AudioStreamPlayback> instance_playback() {
+ Ref<AudioStreamPlaybackOpus> pb = memnew( AudioStreamPlaybackOpus );
+ pb->set_file(file);
+ return pb;
+ }
+
+ void set_file(const String& p_file) { file=p_file; }
+
+};
+
+class ResourceFormatLoaderAudioStreamOpus: public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+#endif // AUDIO_STREAM_OPUS_H
diff --git a/modules/opus/config.py b/modules/opus/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/opus/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/opus/register_types.cpp b/modules/opus/register_types.cpp
new file mode 100644
index 0000000000..a4d71a52c7
--- /dev/null
+++ b/modules/opus/register_types.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "audio_stream_opus.h"
+
+static ResourceFormatLoaderAudioStreamOpus *opus_stream_loader = NULL;
+
+void register_opus_types() {
+
+ opus_stream_loader = memnew( ResourceFormatLoaderAudioStreamOpus );
+ ResourceLoader::add_resource_format_loader(opus_stream_loader);
+ ObjectTypeDB::register_type<AudioStreamOpus>();
+}
+
+void unregister_opus_types() {
+
+ memdelete( opus_stream_loader );
+}
diff --git a/modules/opus/register_types.h b/modules/opus/register_types.h
new file mode 100644
index 0000000000..f4386c373a
--- /dev/null
+++ b/modules/opus/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_opus_types();
+void unregister_opus_types();
diff --git a/modules/pbm/SCsub b/modules/pbm/SCsub
new file mode 100644
index 0000000000..fa328be025
--- /dev/null
+++ b/modules/pbm/SCsub
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_pbm = env_modules.Clone()
+
+env_pbm.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/pbm/bitmap_loader_pbm.cpp b/modules/pbm/bitmap_loader_pbm.cpp
new file mode 100644
index 0000000000..1d08b10824
--- /dev/null
+++ b/modules/pbm/bitmap_loader_pbm.cpp
@@ -0,0 +1,252 @@
+/*************************************************************************/
+/* bitmap_loader_pbm.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "bitmap_loader_pbm.h"
+#include "os/file_access.h"
+#include "scene/resources/bit_mask.h"
+
+
+static bool _get_token(FileAccessRef& f,uint8_t &saved,DVector<uint8_t>& r_token,bool p_binary=false,bool p_single_chunk=false) {
+
+
+ int token_max = r_token.size();
+ DVector<uint8_t>::Write w;
+ if (token_max)
+ w=r_token.write();
+ int ofs=0;
+ bool lf=false;
+
+
+ while(true) {
+
+ uint8_t b;
+ if (saved) {
+ b=saved;
+ saved=0;
+ } else {
+ b = f->get_8();
+ }
+ if (f->eof_reached()) {
+ if (ofs) {
+ w=DVector<uint8_t>::Write();
+ r_token.resize(ofs);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!ofs && !p_binary && b=='#') {
+ //skip comment
+ while(b!='\n') {
+ if (f->eof_reached()) {
+ return false;
+ }
+
+ b = f->get_8();
+ }
+
+ lf=true;
+
+ } else if (b<=32 && !(p_binary && (ofs || lf))) {
+
+ if (b=='\n') {
+ lf=true;
+ }
+
+
+ if (ofs && !p_single_chunk) {
+ w=DVector<uint8_t>::Write();
+ r_token.resize(ofs);
+ saved=b;
+
+ return true;
+ }
+ } else {
+
+ bool resized=false;
+ while (ofs>=token_max) {
+ if (token_max)
+ token_max<<=1;
+ else
+ token_max=1;
+ resized=true;
+ }
+ if (resized) {
+ w=DVector<uint8_t>::Write();
+ r_token.resize(token_max);
+ w=r_token.write();
+ }
+ w[ofs++]=b;
+ }
+ }
+
+ return false;
+}
+
+static int _get_number_from_token(DVector<uint8_t>& r_token) {
+
+ int len = r_token.size();
+ DVector<uint8_t>::Read r = r_token.read();
+ return String::to_int((const char*)r.ptr(),len);
+
+}
+
+
+RES ResourceFormatPBM::load(const String &p_path,const String& p_original_path,Error *r_error) {
+
+#define _RETURN(m_err)\
+{\
+ if (r_error)\
+ *r_error=m_err;\
+ ERR_FAIL_V(RES());\
+}
+
+
+ FileAccessRef f=FileAccess::open(p_path,FileAccess::READ);
+ uint8_t saved=0;
+ if (!f)
+ _RETURN(ERR_CANT_OPEN);
+
+ DVector<uint8_t> token;
+
+ if (!_get_token(f,saved,token)) {
+ _RETURN(ERR_PARSE_ERROR);
+ }
+
+ if (token.size()!=2) {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+ if (token[0]!='P') {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+ if (token[1]!='1' && token[1]!='4') {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+
+ bool bits = token[1]=='4';
+
+ if (!_get_token(f,saved,token)) {
+ _RETURN(ERR_PARSE_ERROR);
+ }
+
+ int width = _get_number_from_token(token);
+ if (width<=0) {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+
+
+ if (!_get_token(f,saved,token)) {
+ _RETURN(ERR_PARSE_ERROR);
+ }
+
+ int height = _get_number_from_token(token);
+ if (height<=0) {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+
+
+ Ref<BitMap> bm;
+ bm.instance();
+ bm->create(Size2i(width,height));
+
+ if (!bits) {
+
+ int required_bytes = width*height;
+ if (!_get_token(f,saved,token,false,true)) {
+ _RETURN(ERR_PARSE_ERROR);
+ }
+
+ if (token.size()<required_bytes) {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+
+ DVector<uint8_t>::Read r=token.read();
+
+ for(int i=0;i<height;i++) {
+ for(int j=0;j<width;j++) {
+
+
+ char num = r[i*width+j];
+ bm->set_bit(Point2i(j,i),num=='0');
+ }
+
+ }
+
+
+
+ } else {
+ //a single, entire token of bits!
+ if (!_get_token(f,saved,token,true)) {
+ _RETURN(ERR_PARSE_ERROR);
+ }
+ int required_bytes = Math::ceil((width*height)/8.0);
+ if (token.size()<required_bytes) {
+ _RETURN(ERR_FILE_CORRUPT);
+ }
+
+ DVector<uint8_t>::Read r=token.read();
+ int bitwidth = width;
+ if (bitwidth % 8)
+ bitwidth+=8-(bitwidth%8);
+
+ for(int i=0;i<height;i++) {
+ for(int j=0;j<width;j++) {
+
+ int ofs = bitwidth*i+j;
+
+ uint8_t byte = r[ofs/8];
+ bool bit = (byte>>(7-(ofs%8)))&1;
+
+ bm->set_bit(Point2i(j,i),!bit);
+
+ }
+
+ }
+
+ }
+
+ return bm;
+
+
+}
+
+void ResourceFormatPBM::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("pbm");
+}
+bool ResourceFormatPBM::handles_type(const String& p_type) const {
+ return p_type=="BitMap";
+}
+String ResourceFormatPBM::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="pbm")
+ return "BitMap";
+ return "";
+}
+
+
diff --git a/modules/pbm/bitmap_loader_pbm.h b/modules/pbm/bitmap_loader_pbm.h
new file mode 100644
index 0000000000..4f7144b3e0
--- /dev/null
+++ b/modules/pbm/bitmap_loader_pbm.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* bitmap_loader_pbm.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef BITMAP_LOADER_PBM_H
+#define BITMAP_LOADER_PBM_H
+
+#include "io/resource_loader.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class ResourceFormatPBM : public ResourceFormatLoader {
+
+
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+
+
+#endif
diff --git a/modules/pbm/config.py b/modules/pbm/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/pbm/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/pbm/register_types.cpp b/modules/pbm/register_types.cpp
new file mode 100644
index 0000000000..181083773a
--- /dev/null
+++ b/modules/pbm/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "bitmap_loader_pbm.h"
+
+static ResourceFormatPBM * pbm_loader = NULL;
+
+void register_pbm_types() {
+
+ pbm_loader = memnew( ResourceFormatPBM );
+ ResourceLoader::add_resource_format_loader(pbm_loader);
+}
+
+void unregister_pbm_types() {
+
+ memdelete( pbm_loader );
+}
diff --git a/modules/pbm/register_types.h b/modules/pbm/register_types.h
new file mode 100644
index 0000000000..20c8133c2c
--- /dev/null
+++ b/modules/pbm/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_pbm_types();
+void unregister_pbm_types();
diff --git a/modules/pvr/SCsub b/modules/pvr/SCsub
new file mode 100644
index 0000000000..4ead52f82f
--- /dev/null
+++ b/modules/pvr/SCsub
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_pvr = env_modules.Clone()
+
+# Thirdparty source files
+# Not unbundled so far since not widespread as shared library
+thirdparty_dir = "#thirdparty/pvrtccompressor/"
+thirdparty_sources = [
+ "BitScale.cpp",
+ "MortonTable.cpp",
+ "PvrTcDecoder.cpp",
+ "PvrTcEncoder.cpp",
+ "PvrTcPacket.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_pvr.add_source_files(env.modules_sources, thirdparty_sources)
+env_pvr.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_pvr.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/pvr/config.py b/modules/pvr/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/pvr/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/pvr/register_types.cpp b/modules/pvr/register_types.cpp
new file mode 100644
index 0000000000..e5e18fb3d1
--- /dev/null
+++ b/modules/pvr/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "texture_loader_pvr.h"
+
+static ResourceFormatPVR *resource_loader_pvr = NULL;
+
+void register_pvr_types() {
+
+ resource_loader_pvr = memnew( ResourceFormatPVR );
+ ResourceLoader::add_resource_format_loader(resource_loader_pvr);
+}
+
+void unregister_pvr_types() {
+
+ memdelete(resource_loader_pvr);
+}
diff --git a/modules/pvr/register_types.h b/modules/pvr/register_types.h
new file mode 100644
index 0000000000..d600f54d51
--- /dev/null
+++ b/modules/pvr/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_pvr_types();
+void unregister_pvr_types();
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
new file mode 100644
index 0000000000..3ab3240512
--- /dev/null
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -0,0 +1,711 @@
+/*************************************************************************/
+/* texture_loader_pvr.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "texture_loader_pvr.h"
+#include "os/file_access.h"
+#include <string.h>
+#include "PvrTcEncoder.h"
+#include "RgbaBitmap.h"
+
+static void _pvrtc_decompress(Image* p_img);
+
+enum PVRFLags {
+
+ PVR_HAS_MIPMAPS=0x00000100,
+ PVR_TWIDDLED=0x00000200,
+ PVR_NORMAL_MAP=0x00000400,
+ PVR_BORDER=0x00000800,
+ PVR_CUBE_MAP=0x00001000,
+ PVR_FALSE_MIPMAPS=0x00002000,
+ PVR_VOLUME_TEXTURES=0x00004000,
+ PVR_HAS_ALPHA=0x00008000,
+ PVR_VFLIP=0x00010000
+
+};
+
+
+
+RES ResourceFormatPVR::load(const String &p_path,const String& p_original_path,Error *r_error) {
+
+ if (r_error)
+ *r_error=ERR_CANT_OPEN;
+
+ Error err;
+ FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err);
+ if (!f)
+ return RES();
+
+ FileAccessRef faref(f);
+
+ ERR_FAIL_COND_V(err,RES());
+
+ if (r_error)
+ *r_error=ERR_FILE_CORRUPT;
+
+ uint32_t hsize = f->get_32();
+
+ ERR_FAIL_COND_V(hsize!=52,RES());
+ uint32_t height = f->get_32();
+ uint32_t width = f->get_32();
+ uint32_t mipmaps = f->get_32();
+ uint32_t flags = f->get_32();
+ uint32_t surfsize = f->get_32();
+ uint32_t bpp = f->get_32();
+ uint32_t rmask = f->get_32();
+ uint32_t gmask = f->get_32();
+ uint32_t bmask = f->get_32();
+ uint32_t amask = f->get_32();
+ uint8_t pvrid[5]={0,0,0,0,0};
+ f->get_buffer(pvrid,4);
+ ERR_FAIL_COND_V(String((char*)pvrid)!="PVR!",RES());
+ uint32_t surfcount = f->get_32();
+
+/*
+ print_line("height: "+itos(height));
+ print_line("width: "+itos(width));
+ print_line("mipmaps: "+itos(mipmaps));
+ print_line("flags: "+itos(flags));
+ print_line("surfsize: "+itos(surfsize));
+ print_line("bpp: "+itos(bpp));
+ print_line("rmask: "+itos(rmask));
+ print_line("gmask: "+itos(gmask));
+ print_line("bmask: "+itos(bmask));
+ print_line("amask: "+itos(amask));
+ print_line("surfcount: "+itos(surfcount));
+*/
+
+ DVector<uint8_t> data;
+ data.resize(surfsize);
+
+ ERR_FAIL_COND_V(data.size()==0,RES());
+
+
+ DVector<uint8_t>::Write w = data.write();
+ f->get_buffer(&w[0],surfsize);
+ err = f->get_error();
+ ERR_FAIL_COND_V(err!=OK,RES());
+
+ Image::Format format=Image::FORMAT_MAX;
+
+
+ switch(flags&0xFF) {
+
+ case 0x18:
+ case 0xC: format=(flags&PVR_HAS_ALPHA)?Image::FORMAT_PVRTC2_ALPHA:Image::FORMAT_PVRTC2; break;
+ case 0x19:
+ case 0xD: format=(flags&PVR_HAS_ALPHA)?Image::FORMAT_PVRTC4_ALPHA:Image::FORMAT_PVRTC4; break;
+ case 0x16:
+ format=Image::FORMAT_GRAYSCALE; break;
+ case 0x17:
+ format=Image::FORMAT_GRAYSCALE_ALPHA; break;
+ case 0x20:
+ case 0x80:
+ case 0x81:
+ format=Image::FORMAT_BC1; break;
+ case 0x21:
+ case 0x22:
+ case 0x82:
+ case 0x83:
+ format=Image::FORMAT_BC2; break;
+ case 0x23:
+ case 0x24:
+ case 0x84:
+ case 0x85:
+ format=Image::FORMAT_BC3; break;
+ case 0x4:
+ case 0x15:
+ format=Image::FORMAT_RGB; break;
+ case 0x5:
+ case 0x12:
+ format=Image::FORMAT_RGBA; break;
+ case 0x36:
+ format=Image::FORMAT_ETC; break;
+ default:
+ ERR_EXPLAIN("Unsupported format in PVR texture: "+itos(flags&0xFF));
+ ERR_FAIL_V(RES());
+
+ }
+
+ w = DVector<uint8_t>::Write();
+
+ int tex_flags=Texture::FLAG_FILTER|Texture::FLAG_REPEAT;
+
+ if (mipmaps)
+ tex_flags|=Texture::FLAG_MIPMAPS;
+
+
+ print_line("flip: "+itos(flags&PVR_VFLIP));
+
+ Image image(width,height,mipmaps,format,data);
+ ERR_FAIL_COND_V(image.empty(),RES());
+
+ Ref<ImageTexture> texture = memnew( ImageTexture );
+ texture->create_from_image(image,tex_flags);
+
+ if (r_error)
+ *r_error=OK;
+
+ return texture;
+
+}
+
+void ResourceFormatPVR::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("pvr");
+}
+bool ResourceFormatPVR::handles_type(const String& p_type) const {
+
+ return ObjectTypeDB::is_type(p_type,"Texture");
+}
+String ResourceFormatPVR::get_resource_type(const String &p_path) const {
+
+
+ if (p_path.extension().to_lower()=="pvr")
+ return "Texture";
+ return "";
+}
+
+
+
+static void _compress_pvrtc4(Image * p_img) {
+
+ Image img = *p_img;
+
+ bool make_mipmaps=false;
+ if (img.get_width()%8 || img.get_height()%8) {
+ make_mipmaps=img.get_mipmaps()>0;
+ img.resize(img.get_width()+(8-(img.get_width()%8)),img.get_height()+(8-(img.get_height()%8)));
+ }
+ img.convert(Image::FORMAT_RGBA);
+ if (img.get_mipmaps()==0 && make_mipmaps)
+ img.generate_mipmaps();
+
+ bool use_alpha=img.detect_alpha();
+
+ Image new_img;
+ new_img.create(img.get_width(),img.get_height(),true,use_alpha?Image::FORMAT_PVRTC4_ALPHA:Image::FORMAT_PVRTC4);
+ DVector<uint8_t> data=new_img.get_data();
+ {
+ DVector<uint8_t>::Write wr=data.write();
+ DVector<uint8_t>::Read r=img.get_data().read();
+
+
+ for(int i=0;i<=new_img.get_mipmaps();i++) {
+
+ int ofs,size,w,h;
+ img.get_mipmap_offset_size_and_dimensions(i,ofs,size,w,h);
+ Javelin::RgbaBitmap bm(w,h);
+ copymem(bm.GetData(),&r[ofs],size);
+ {
+ Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
+ for(int j=0;j<size/4;j++) {
+ SWAP(dp[j].r,dp[j].b);
+ }
+ }
+
+ new_img.get_mipmap_offset_size_and_dimensions(i,ofs,size,w,h);
+ Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs],bm);
+ }
+
+ }
+
+ *p_img = Image(new_img.get_width(),new_img.get_height(),new_img.get_mipmaps(),new_img.get_format(),data);
+
+}
+
+ResourceFormatPVR::ResourceFormatPVR() {
+
+
+ Image::_image_decompress_pvrtc=_pvrtc_decompress;
+ Image::_image_compress_pvrtc4_func=_compress_pvrtc4;
+ Image::_image_compress_pvrtc2_func=_compress_pvrtc4;
+
+}
+
+/////////////////////////////////////////////////////////
+
+//PVRTC decompressor, Based on PVRTC decompressor by IMGTEC.
+
+/////////////////////////////////////////////////////////
+
+
+#define PT_INDEX 2
+#define BLK_Y_SIZE 4
+#define BLK_X_MAX 8
+#define BLK_X_2BPP 8
+#define BLK_X_4BPP 4
+
+#define WRAP_COORD(Val, Size) ((Val) & ((Size)-1))
+
+/*
+ Define an expression to either wrap or clamp large or small vals to the
+ legal coordinate range
+*/
+#define LIMIT_COORD(Val, Size, p_tiled) \
+ ((p_tiled)? WRAP_COORD((Val), (Size)): CLAMP((Val), 0, (Size)-1))
+
+
+struct PVRTCBlock {
+ //blocks are 64 bits
+ uint32_t data[2];
+};
+
+
+
+_FORCE_INLINE_ bool is_po2( uint32_t p_input ) {
+
+ if( p_input==0 )
+ return 0;
+ uint32_t minus1=p_input- 1;
+ return ((p_input|minus1)==(p_input^minus1))?1:0;
+}
+
+
+static void unpack_5554(const PVRTCBlock *p_block, int p_ab_colors[2][4]) {
+
+ uint32_t raw_bits[2];
+ raw_bits[0] = p_block->data[1] & (0xFFFE);
+ raw_bits[1] = p_block->data[1] >> 16;
+
+ for(int i=0;i<2;i++) {
+
+ if(raw_bits[i] & (1<<15)) {
+
+ p_ab_colors[i][0]= (raw_bits[i] >> 10) & 0x1F;
+ p_ab_colors[i][1]= (raw_bits[i] >> 5) & 0x1F;
+ p_ab_colors[i][2]= raw_bits[i] & 0x1F;
+ if(i==0)
+ p_ab_colors[0][2]|= p_ab_colors[0][2] >> 4;
+ p_ab_colors[i][3] = 0xF;
+ } else {
+
+ p_ab_colors[i][0] = (raw_bits[i] >> (8-1)) & 0x1E;
+ p_ab_colors[i][1] = (raw_bits[i] >> (4-1)) & 0x1E;
+
+ p_ab_colors[i][0] |= p_ab_colors[i][0] >> 4;
+ p_ab_colors[i][1] |= p_ab_colors[i][1] >> 4;
+
+ p_ab_colors[i][2] = (raw_bits[i] & 0xF) << 1;
+
+ if(i==0)
+ p_ab_colors[0][2] |= p_ab_colors[0][2] >> 3;
+ else
+ p_ab_colors[0][2] |= p_ab_colors[0][2] >> 4;
+
+ p_ab_colors[i][3] = (raw_bits[i] >> 11) & 0xE;
+ }
+ }
+}
+
+
+static void unpack_modulations(const PVRTCBlock *p_block, const int p_2bit, int p_modulation[8][16], int p_modulation_modes[8][16], int p_x, int p_y) {
+
+ int block_mod_mode = p_block->data[1] & 1;
+ uint32_t modulation_bits = p_block->data[0];
+
+ if(p_2bit && block_mod_mode) {
+
+ for(int y = 0; y < BLK_Y_SIZE; y++) {
+ for(int x = 0; x < BLK_X_2BPP; x++) {
+
+ p_modulation_modes[y+p_y][x+p_x] = block_mod_mode;
+
+ if(((x^y)&1) == 0) {
+ p_modulation[y+p_y][x+p_x] = modulation_bits & 3;
+ modulation_bits >>= 2;
+ }
+ }
+ }
+
+ } else if(p_2bit) {
+
+ for(int y = 0; y < BLK_Y_SIZE; y++) {
+ for(int x = 0; x < BLK_X_2BPP; x++) {
+ p_modulation_modes[y+p_y][x+p_x] = block_mod_mode;
+
+ if(modulation_bits & 1)
+ p_modulation[y+p_y][x+p_x] = 0x3;
+ else
+ p_modulation[y+p_y][x+p_x] = 0x0;
+
+ modulation_bits >>= 1;
+ }
+ }
+ } else {
+ for(int y = 0; y < BLK_Y_SIZE; y++) {
+ for(int x = 0; x < BLK_X_4BPP; x++) {
+ p_modulation_modes[y+p_y][x+p_x] = block_mod_mode;
+ p_modulation[y+p_y][x+p_x] = modulation_bits & 3;
+ modulation_bits >>= 2;
+ }
+ }
+ }
+
+ ERR_FAIL_COND(modulation_bits!=0);
+}
+
+
+
+static void interpolate_colors(const int p_colorp[4], const int p_colorq[4], const int p_colorr[4], const int p_colors[4], bool p_2bit, const int x, const int y, int r_result[4]) {
+ int u, v, uscale;
+ int k;
+
+ int tmp1, tmp2;
+
+ int P[4], Q[4], R[4], S[4];
+
+ for(k = 0; k < 4; k++) {
+ P[k] = p_colorp[k];
+ Q[k] = p_colorq[k];
+ R[k] = p_colorr[k];
+ S[k] = p_colors[k];
+ }
+
+ v = (y & 0x3) | ((~y & 0x2) << 1);
+
+ if(p_2bit)
+ u = (x & 0x7) | ((~x & 0x4) << 1);
+ else
+ u = (x & 0x3) | ((~x & 0x2) << 1);
+
+ v = v - BLK_Y_SIZE/2;
+
+ if(p_2bit) {
+ u = u - BLK_X_2BPP/2;
+ uscale = 8;
+ } else {
+ u = u - BLK_X_4BPP/2;
+ uscale = 4;
+ }
+
+ for(k = 0; k < 4; k++) {
+ tmp1 = P[k] * uscale + u * (Q[k] - P[k]);
+ tmp2 = R[k] * uscale + u * (S[k] - R[k]);
+
+ tmp1 = tmp1 * 4 + v * (tmp2 - tmp1);
+
+ r_result[k] = tmp1;
+ }
+
+ if(p_2bit) {
+ for(k = 0; k < 3; k++) {
+ r_result[k] >>= 2;
+ }
+
+ r_result[3] >>= 1;
+ } else {
+ for(k = 0; k < 3; k++) {
+ r_result[k] >>= 1;
+ }
+ }
+
+ for(k = 0; k < 4; k++) {
+ ERR_FAIL_COND(r_result[k] >= 256);
+ }
+
+
+ for(k = 0; k < 3; k++) {
+ r_result[k] += r_result[k] >> 5;
+ }
+
+ r_result[3] += r_result[3] >> 4;
+
+ for(k = 0; k < 4; k++) {
+ ERR_FAIL_COND(r_result[k] >= 256);
+ }
+
+}
+
+
+static void get_modulation_value(int x, int y, const int p_2bit, const int p_modulation[8][16], const int p_modulation_modes[8][16], int *r_mod, int *p_dopt)
+{
+ static const int rep_vals0[4] = {0, 3, 5, 8};
+ static const int rep_vals1[4] = {0, 4, 4, 8};
+
+ int mod_val;
+
+ y = (y & 0x3) | ((~y & 0x2) << 1);
+
+ if(p_2bit)
+ x = (x & 0x7) | ((~x & 0x4) << 1);
+ else
+ x = (x & 0x3) | ((~x & 0x2) << 1);
+
+ *p_dopt = 0;
+
+ if(p_modulation_modes[y][x]==0) {
+ mod_val = rep_vals0[p_modulation[y][x]];
+ } else if(p_2bit) {
+ if(((x^y)&1)==0)
+ mod_val = rep_vals0[p_modulation[y][x]];
+ else if(p_modulation_modes[y][x] == 1) {
+ mod_val = (rep_vals0[p_modulation[y-1][x]] +
+ rep_vals0[p_modulation[y+1][x]] +
+ rep_vals0[p_modulation[y][x-1]] +
+ rep_vals0[p_modulation[y][x+1]] + 2) / 4;
+ } else if(p_modulation_modes[y][x] == 2) {
+ mod_val = (rep_vals0[p_modulation[y][x-1]] +
+ rep_vals0[p_modulation[y][x+1]] + 1) / 2;
+ } else {
+ mod_val = (rep_vals0[p_modulation[y-1][x]] +
+ rep_vals0[p_modulation[y+1][x]] + 1) / 2;
+ }
+ } else {
+ mod_val = rep_vals1[p_modulation[y][x]];
+
+ *p_dopt = p_modulation[y][x] == PT_INDEX;
+ }
+
+ *r_mod =mod_val;
+}
+
+
+static int disable_twiddling = 0;
+
+static uint32_t twiddle_uv(uint32_t p_height, uint32_t p_width, uint32_t p_y, uint32_t p_x) {
+
+ uint32_t twiddled;
+
+ uint32_t min_dimension;
+ uint32_t max_value;
+
+ uint32_t scr_bit_pos;
+ uint32_t dst_bit_pos;
+
+ int shift_count;
+
+ ERR_FAIL_COND_V(p_y >= p_height,0);
+ ERR_FAIL_COND_V(p_x >= p_width,0);
+
+ ERR_FAIL_COND_V(!is_po2(p_height),0);
+ ERR_FAIL_COND_V(!is_po2(p_width),0);
+
+ if(p_height < p_width) {
+ min_dimension = p_height;
+ max_value = p_x;
+ } else {
+ min_dimension = p_width;
+ max_value = p_y;
+ }
+
+ if(disable_twiddling)
+ return (p_y* p_width + p_x);
+
+ scr_bit_pos = 1;
+ dst_bit_pos = 1;
+ twiddled = 0;
+ shift_count = 0;
+
+ while(scr_bit_pos < min_dimension) {
+ if(p_y & scr_bit_pos) {
+ twiddled |= dst_bit_pos;
+ }
+
+ if(p_x & scr_bit_pos) {
+ twiddled |= (dst_bit_pos << 1);
+ }
+
+ scr_bit_pos <<= 1;
+ dst_bit_pos <<= 2;
+ shift_count += 1;
+
+ }
+
+ max_value >>= shift_count;
+
+ twiddled |= (max_value << (2*shift_count));
+
+ return twiddled;
+}
+
+static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int p_width, const int p_height, const int p_tiled, unsigned char* p_dst) {
+ int x, y;
+ int i, j;
+
+ int block_x, blk_y;
+ int block_xp1, blk_yp1;
+ int x_block_size;
+ int block_width, block_height;
+
+ int p_x, p_y;
+
+ int p_modulation[8][16];
+ int p_modulation_modes[8][16];
+
+ int Mod, DoPT;
+
+ unsigned int u_pos;
+
+ // local neighbourhood of blocks
+ PVRTCBlock *p_blocks[2][2];
+
+ PVRTCBlock *prev[2][2] = {{NULL, NULL}, {NULL, NULL}};
+
+ struct
+ {
+ int Reps[2][4];
+ }colors5554[2][2];
+
+
+ int ASig[4], BSig[4];
+
+ int r_result[4];
+
+ if(p_2bit)
+ x_block_size = BLK_X_2BPP;
+ else
+ x_block_size = BLK_X_4BPP;
+
+
+ block_width = MAX(2, p_width / x_block_size);
+ block_height = MAX(2, p_height / BLK_Y_SIZE);
+
+ for(y = 0; y < p_height; y++)
+ {
+ for(x = 0; x < p_width; x++)
+ {
+
+ block_x = (x - x_block_size/2);
+ blk_y = (y - BLK_Y_SIZE/2);
+
+ block_x = LIMIT_COORD(block_x, p_width, p_tiled);
+ blk_y = LIMIT_COORD(blk_y, p_height, p_tiled);
+
+
+ block_x /= x_block_size;
+ blk_y /= BLK_Y_SIZE;
+
+ block_xp1 = LIMIT_COORD(block_x+1, block_width, p_tiled);
+ blk_yp1 = LIMIT_COORD(blk_y+1, block_height, p_tiled);
+
+ p_blocks[0][0] = p_comp_img +twiddle_uv(block_height, block_width, blk_y, block_x);
+ p_blocks[0][1] = p_comp_img +twiddle_uv(block_height, block_width, blk_y, block_xp1);
+ p_blocks[1][0] = p_comp_img +twiddle_uv(block_height, block_width, blk_yp1, block_x);
+ p_blocks[1][1] = p_comp_img +twiddle_uv(block_height, block_width, blk_yp1, block_xp1);
+
+ if(memcmp(prev, p_blocks, 4*sizeof(void*)) != 0) {
+ p_y = 0;
+ for(i = 0; i < 2; i++) {
+ p_x = 0;
+ for(j = 0; j < 2; j++) {
+ unpack_5554(p_blocks[i][j], colors5554[i][j].Reps);
+
+ unpack_modulations(p_blocks[i][j],
+ p_2bit,
+ p_modulation,
+ p_modulation_modes,
+ p_x, p_y);
+
+ p_x += x_block_size;
+ }
+
+ p_y += BLK_Y_SIZE;
+ }
+
+
+ memcpy(prev, p_blocks, 4*sizeof(void*));
+ }
+
+
+ interpolate_colors(colors5554[0][0].Reps[0],
+ colors5554[0][1].Reps[0],
+ colors5554[1][0].Reps[0],
+ colors5554[1][1].Reps[0],
+ p_2bit, x, y,
+ ASig);
+
+ interpolate_colors(colors5554[0][0].Reps[1],
+ colors5554[0][1].Reps[1],
+ colors5554[1][0].Reps[1],
+ colors5554[1][1].Reps[1],
+ p_2bit, x, y,
+ BSig);
+
+ get_modulation_value(x,y, p_2bit, (const int (*)[16])p_modulation, (const int (*)[16])p_modulation_modes,
+ &Mod, &DoPT);
+
+ for(i = 0; i < 4; i++) {
+ r_result[i] = ASig[i] * 8 + Mod * (BSig[i] - ASig[i]);
+ r_result[i] >>= 3;
+ }
+
+ if(DoPT)
+ r_result[3] = 0;
+
+
+ u_pos = (x+y*p_width)<<2;
+ p_dst[u_pos+0] = (uint8_t)r_result[0];
+ p_dst[u_pos+1] = (uint8_t)r_result[1];
+ p_dst[u_pos+2] = (uint8_t)r_result[2];
+ p_dst[u_pos+3] = (uint8_t)r_result[3];
+ }
+ }
+}
+
+static void _pvrtc_decompress(Image* p_img) {
+
+// static void decompress_pvrtc(const void *p_comp_img, const int p_2bit, const int p_width, const int p_height, unsigned char* p_dst) {
+// decompress_pvrtc((PVRTCBlock*)p_comp_img,p_2bit,p_width,p_height,1,p_dst);
+// }
+
+ ERR_FAIL_COND( p_img->get_format()!=Image::FORMAT_PVRTC2 && p_img->get_format()!=Image::FORMAT_PVRTC2_ALPHA && p_img->get_format()!=Image::FORMAT_PVRTC4 && p_img->get_format()!=Image::FORMAT_PVRTC4_ALPHA);
+
+ bool _2bit = (p_img->get_format()==Image::FORMAT_PVRTC2 || p_img->get_format()==Image::FORMAT_PVRTC2_ALPHA );
+
+ DVector<uint8_t> data = p_img->get_data();
+ DVector<uint8_t>::Read r = data.read();
+
+
+ DVector<uint8_t> newdata;
+ newdata.resize( p_img->get_width() * p_img->get_height() * 4);
+ DVector<uint8_t>::Write w=newdata.write();
+
+ decompress_pvrtc((PVRTCBlock*)r.ptr(),_2bit,p_img->get_width(),p_img->get_height(),0,(unsigned char*)w.ptr());
+
+ //for(int i=0;i<newdata.size();i++) {
+ // print_line(itos(w[i]));
+ //}
+
+ w=DVector<uint8_t>::Write();
+ r=DVector<uint8_t>::Read();
+
+ bool make_mipmaps=p_img->get_mipmaps()>0;
+ Image newimg(p_img->get_width(),p_img->get_height(),0,Image::FORMAT_RGBA,newdata);
+ if (make_mipmaps)
+ newimg.generate_mipmaps();
+ *p_img=newimg;
+
+}
+
+
+
+
+
+
+
+
diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h
new file mode 100644
index 0000000000..5efb3b2507
--- /dev/null
+++ b/modules/pvr/texture_loader_pvr.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* texture_loader_pvr.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef TEXTURE_LOADER_PVR_H
+#define TEXTURE_LOADER_PVR_H
+
+
+#include "scene/resources/texture.h"
+#include "io/resource_loader.h"
+
+
+class ResourceFormatPVR : public ResourceFormatLoader{
+public:
+
+ virtual RES load(const String &p_path,const String& p_original_path,Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+ ResourceFormatPVR();
+ virtual ~ResourceFormatPVR() {}
+};
+
+
+#endif // TEXTURE_LOADER_PVR_H
diff --git a/modules/regex/SCsub b/modules/regex/SCsub
new file mode 100644
index 0000000000..0882406761
--- /dev/null
+++ b/modules/regex/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/regex/config.py b/modules/regex/config.py
new file mode 100644
index 0000000000..667b5d8ba6
--- /dev/null
+++ b/modules/regex/config.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
+
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
new file mode 100644
index 0000000000..a0f4b4934c
--- /dev/null
+++ b/modules/regex/regex.cpp
@@ -0,0 +1,1502 @@
+/*************************************************************************/
+/* regex.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "regex.h"
+#include <wctype.h>
+#include <wchar.h>
+
+static int RegEx_hex2int(const CharType c)
+{
+ if ('0' <= c && c <= '9')
+ return int(c - '0');
+ else if ('a' <= c && c <= 'f')
+ return int(c - 'a') + 10;
+ else if ('A' <= c && c <= 'F')
+ return int(c - 'A') + 10;
+ return -1;
+}
+
+struct RegExSearch {
+
+ Ref<RegExMatch> match;
+ const CharType* str;
+ int end;
+ int eof;
+
+ // For standard quantifier behaviour, test_parent is used to check the
+ // rest of the pattern. If the pattern matches, to prevent the parent
+ // from testing again, the complete flag is used as a shortcut out.
+ bool complete;
+
+ // With lookahead, the position needs to rewind to its starting position
+ // when test_parent is used. Due to functional programming, this state
+ // has to be kept as a parameter.
+ Vector<int> lookahead_pos;
+
+ CharType at(int p_pos) {
+ return str[p_pos];
+ }
+
+ RegExSearch(Ref<RegExMatch>& p_match, int p_end, int p_lookahead) : match(p_match) {
+
+ str = p_match->string.c_str();
+ end = p_end;
+ eof = p_match->string.length();
+ complete = false;
+ lookahead_pos.resize(p_lookahead);
+ }
+
+};
+
+struct RegExNode {
+
+ RegExNode* next;
+ RegExNode* previous;
+ RegExNode* parent;
+ bool quantifiable;
+ int length;
+
+ RegExNode() {
+
+ next = NULL;
+ previous = NULL;
+ parent = NULL;
+ quantifiable = false;
+ length = -1;
+ }
+
+ virtual ~RegExNode() {
+
+ if (next)
+ memdelete(next);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return next ? next->test(s, pos) : -1;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ if (next)
+ pos = next->test(s, pos);
+
+ if (pos >= 0) {
+ s.complete = true;
+ if (parent)
+ pos = parent->test_parent(s, pos);
+ }
+
+ if (pos < 0)
+ s.complete = false;
+
+ return pos;
+ }
+
+ void increment_length(int amount, bool subtract = false) {
+
+ if (amount >= 0 && length >= 0) {
+ if (!subtract)
+ length += amount;
+ else
+ length -= amount;
+ } else {
+ length = -1;
+ }
+
+ if (parent)
+ parent->increment_length(amount, subtract);
+
+ }
+
+};
+
+struct RegExNodeChar : public RegExNode {
+
+ CharType ch;
+
+ RegExNodeChar(CharType p_char) {
+
+ length = 1;
+ quantifiable = true;
+ ch = p_char;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos || s.at(pos) != ch)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+ static CharType parse_escape(const CharType*& c) {
+
+ int point = 0;
+ switch (c[1]) {
+ case 'x':
+ for (int i = 2; i <= 3; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[3];
+ return CharType(point);
+ case 'u':
+ for (int i = 2; i <= 5; ++i) {
+ int res = RegEx_hex2int(c[i]);
+ if (res == -1)
+ return '\0';
+ point = (point << 4) + res;
+ }
+ c = &c[5];
+ return CharType(point);
+ case '0': ++c; return '\0';
+ case 'a': ++c; return '\a';
+ case 'e': ++c; return '\e';
+ case 'f': ++c; return '\f';
+ case 'n': ++c; return '\n';
+ case 'r': ++c; return '\r';
+ case 't': ++c; return '\t';
+ case 'v': ++c; return '\v';
+ case 'b': ++c; return '\b';
+ default: break;
+ }
+ return (++c)[0];
+ }
+};
+
+struct RegExNodeRange : public RegExNode {
+
+ CharType start;
+ CharType end;
+
+ RegExNodeRange(CharType p_start, CharType p_end) {
+
+ length = 1;
+ quantifiable = true;
+ start = p_start;
+ end = p_end;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ CharType c = s.at(pos);
+ if (c < start || end < c)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeShorthand : public RegExNode {
+
+ CharType repr;
+
+ RegExNodeShorthand(CharType p_repr) {
+
+ length = 1;
+ quantifiable = true;
+ repr = p_repr;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ bool found = false;
+ bool invert = false;
+ CharType c = s.at(pos);
+ switch (repr) {
+ case '.':
+ found = true;
+ break;
+ case 'W':
+ invert = true;
+ case 'w':
+ found = (c == '_' || iswalnum(c) != 0);
+ break;
+ case 'D':
+ invert = true;
+ case 'd':
+ found = ('0' <= c && c <= '9');
+ break;
+ case 'S':
+ invert = true;
+ case 's':
+ found = (iswspace(c) != 0);
+ break;
+ default:
+ break;
+ }
+
+ if (found == invert)
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+};
+
+struct RegExNodeClass : public RegExNode {
+
+ enum Type {
+ Type_none,
+ Type_alnum,
+ Type_alpha,
+ Type_ascii,
+ Type_blank,
+ Type_cntrl,
+ Type_digit,
+ Type_graph,
+ Type_lower,
+ Type_print,
+ Type_punct,
+ Type_space,
+ Type_upper,
+ Type_xdigit,
+ Type_word
+ };
+
+ Type type;
+
+ bool test_class(CharType c) const {
+
+ static Vector<CharType> REGEX_NODE_SPACE = String(" \t\r\n\f");
+ static Vector<CharType> REGEX_NODE_PUNCT = String("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
+
+ switch (type) {
+ case Type_alnum:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_alpha:
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return false;
+ case Type_ascii:
+ return (0x00 <= c && c <= 0x7F);
+ case Type_blank:
+ return (c == ' ' || c == '\t');
+ case Type_cntrl:
+ return ((0x00 <= c && c <= 0x1F) || c == 0x7F);
+ case Type_digit:
+ return ('0' <= c && c <= '9');
+ case Type_graph:
+ return (0x20 < c && c < 0x7F);
+ case Type_lower:
+ return ('a' <= c && c <= 'z');
+ case Type_print:
+ return (0x1F < c && c < 0x1F);
+ case Type_punct:
+ return (REGEX_NODE_PUNCT.find(c) >= 0);
+ case Type_space:
+ return (REGEX_NODE_SPACE.find(c) >= 0);
+ case Type_upper:
+ return ('A' <= c && c <= 'Z');
+ case Type_xdigit:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'f') return true;
+ if ('A' <= c && c <= 'F') return true;
+ return false;
+ case Type_word:
+ if ('0' <= c && c <= '9') return true;
+ if ('a' <= c && c <= 'z') return true;
+ if ('A' <= c && c <= 'Z') return true;
+ return (c == '_');
+ default:
+ return false;
+ }
+ return false;
+ }
+
+ RegExNodeClass(Type p_type) {
+
+ length = 1;
+ quantifiable = true;
+ type = p_type;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (s.end <= pos || 0 > pos)
+ return -1;
+
+ if (!test_class(s.at(pos)))
+ return -1;
+
+ return next ? next->test(s, pos + 1) : pos + 1;
+ }
+
+#define REGEX_CMP_CLASS(POS, NAME) if (cmp_class(POS, #NAME)) return Type_ ## NAME
+
+ static Type parse_type(const CharType*& p_pos) {
+
+ REGEX_CMP_CLASS(p_pos, alnum);
+ REGEX_CMP_CLASS(p_pos, alpha);
+ REGEX_CMP_CLASS(p_pos, ascii);
+ REGEX_CMP_CLASS(p_pos, blank);
+ REGEX_CMP_CLASS(p_pos, cntrl);
+ REGEX_CMP_CLASS(p_pos, digit);
+ REGEX_CMP_CLASS(p_pos, graph);
+ REGEX_CMP_CLASS(p_pos, lower);
+ REGEX_CMP_CLASS(p_pos, print);
+ REGEX_CMP_CLASS(p_pos, punct);
+ REGEX_CMP_CLASS(p_pos, space);
+ REGEX_CMP_CLASS(p_pos, upper);
+ REGEX_CMP_CLASS(p_pos, xdigit);
+ REGEX_CMP_CLASS(p_pos, word);
+ return Type_none;
+ }
+
+ static bool cmp_class(const CharType*& p_pos, const char* p_text) {
+
+ unsigned int i = 0;
+ for (i = 0; p_text[i] != '\0'; ++i)
+ if (p_pos[i] != p_text[i])
+ return false;
+
+ if (p_pos[i++] != ':' || p_pos[i] != ']')
+ return false;
+
+ p_pos = &p_pos[i];
+ return true;
+ }
+};
+
+struct RegExNodeAnchorStart : public RegExNode {
+
+ RegExNodeAnchorStart() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != 0)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeAnchorEnd : public RegExNode {
+
+ RegExNodeAnchorEnd() {
+
+ length = 0;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos != s.eof)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeWordBoundary : public RegExNode {
+
+ bool inverse;
+
+ RegExNodeWordBoundary(bool p_inverse) {
+
+ length = 0;
+ inverse = p_inverse;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ bool left = false;
+ bool right = false;
+
+ if (pos != 0) {
+ CharType c = s.at(pos - 1);
+ if (c == '_' || iswalnum(c))
+ left = true;
+ }
+
+ if (pos != s.eof) {
+ CharType c = s.at(pos);
+ if (c == '_' || iswalnum(c))
+ right = true;
+ }
+
+ if ((left == right) != inverse)
+ return -1;
+
+ return next ? next->test(s, pos) : pos;
+ }
+};
+
+struct RegExNodeQuantifier : public RegExNode {
+
+ int min;
+ int max;
+ bool greedy;
+ RegExNode* child;
+
+ RegExNodeQuantifier(int p_min, int p_max) {
+
+ min = p_min;
+ max = p_max;
+ greedy = true;
+ child = NULL;
+ }
+
+ ~RegExNodeQuantifier() {
+
+ if (child)
+ memdelete(child);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ return test_step(s, pos, 0, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ s.complete = false;
+ return pos;
+ }
+
+ int test_step(RegExSearch& s, int pos, int level, int start) const {
+
+ if (pos > s.end)
+ return -1;
+
+ if (!greedy && level > min) {
+ int res = next ? next->test(s, pos) : pos;
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+
+ if (max >= 0 && level > max)
+ return -1;
+
+ int res = pos;
+ if (level >= 1) {
+ if (level > min + 1 && pos == start)
+ return -1;
+
+ res = child->test(s, pos);
+ if (s.complete)
+ return res;
+ }
+
+ if (res >= 0) {
+
+ int res_step = test_step(s, res, level + 1, start);
+ if (res_step >= 0)
+ return res_step;
+
+ if (greedy && level >= min) {
+ if (next)
+ res = next->test(s, res);
+ if (s.complete)
+ return res;
+
+ if (res >= 0 && parent->test_parent(s, res) >= 0)
+ return res;
+ }
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeBackReference : public RegExNode {
+
+ int id;
+
+ RegExNodeBackReference(int p_id) {
+
+ length = -1;
+ quantifiable = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ for (int i = 0; i < ref.length; ++i) {
+
+ if (pos + i >= s.end)
+ return -1;
+
+ if (s.at(ref.start + i) != s.at(pos + i))
+ return -1;
+ }
+ return next ? next->test(s, pos + ref.length) : pos + ref.length;
+ }
+};
+
+
+struct RegExNodeGroup : public RegExNode {
+
+ bool inverse;
+ bool reset_pos;
+ Vector<RegExNode*> childset;
+ RegExNode* back;
+
+ RegExNodeGroup() {
+
+ length = 0;
+ quantifiable = true;
+ inverse = false;
+ reset_pos = false;
+ back = NULL;
+ }
+
+ virtual ~RegExNodeGroup() {
+
+ for (int i = 0; i < childset.size(); ++i)
+ memdelete(childset[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < childset.size(); ++i) {
+
+ s.complete = false;
+
+ int res = childset[i]->test(s, pos);
+
+ if (s.complete)
+ return res;
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < childset.size())
+ continue;
+ }
+
+ if (res >= 0) {
+ if (reset_pos)
+ res = pos;
+ return next ? next->test(s, res) : res;
+ }
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ node->previous = back;
+
+ if (back)
+ back->next = node;
+ else
+ childset.push_back(node);
+
+ increment_length(node->length);
+
+ back = node;
+ }
+
+ void add_childset() {
+
+ if (childset.size() > 0)
+ length = -1;
+ back = NULL;
+ }
+
+ RegExNode* swap_back(RegExNode* node) {
+
+ RegExNode* old = back;
+
+ if (old) {
+ if (!old->previous)
+ childset.remove(childset.size() - 1);
+ back = old->previous;
+ increment_length(old->length, true);
+ }
+
+ add_child(node);
+
+ return old;
+ }
+};
+
+struct RegExNodeCapturing : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeCapturing(int p_id = 0) {
+
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ int old_start = ref.start;
+ ref.start = pos;
+
+ int res = RegExNodeGroup::test(s, pos);
+
+ if (res >= 0) {
+ if (!s.complete)
+ ref.length = res - pos;
+ } else {
+ ref.start = old_start;
+ }
+
+ return res;
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ RegExMatch::Group& ref = s.match->captures[id];
+ ref.length = pos - ref.start;
+ return RegExNode::test_parent(s, pos);
+ }
+
+ static Variant parse_name(const CharType*& c, bool p_allow_numeric) {
+
+ if (c[1] == '0') {
+ return -1;
+ } else if ('1' <= c[1] && c[1] <= '9') {
+ if (!p_allow_numeric)
+ return -1;
+ int res = (++c)[0] - '0';
+ while ('0' <= c[1] && c[1] <= '9')
+ res = res * 10 + int((++c)[0] - '0');
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ } else if (iswalnum(c[1])) {
+ String res(++c, 1);
+ while (iswalnum(c[1]))
+ res += String(++c, 1);
+ if ((++c)[0] != '>')
+ return -1;
+ return res;
+ }
+ return -1;
+ }
+};
+
+struct RegExNodeLookAhead : public RegExNodeGroup {
+
+ int id;
+
+ RegExNodeLookAhead(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ id = p_id;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ s.lookahead_pos[id] = pos;
+ return RegExNodeGroup::test(s, pos);
+ }
+
+ virtual int test_parent(RegExSearch& s, int pos) const {
+
+ return RegExNode::test_parent(s, s.lookahead_pos[id]);
+ }
+};
+
+struct RegExNodeLookBehind : public RegExNodeGroup {
+
+ RegExNodeLookBehind(bool p_inverse, int p_id = 0) {
+
+ quantifiable = false;
+ inverse = p_inverse;
+ reset_pos = true;
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ if (pos < length)
+ return -1;
+ return RegExNodeGroup::test(s, pos - length);
+ }
+};
+
+struct RegExNodeBracket : public RegExNode {
+
+ bool inverse;
+ Vector<RegExNode*> children;
+
+ RegExNodeBracket() {
+
+ length = 1;
+ quantifiable = true;
+ inverse = false;
+ }
+
+ virtual ~RegExNodeBracket() {
+
+ for (int i = 0; i < children.size(); ++i)
+ memdelete(children[i]);
+ }
+
+ virtual int test(RegExSearch& s, int pos) const {
+
+ for (int i = 0; i < children.size(); ++i) {
+
+ int res = children[i]->test(s, pos);
+
+ if (inverse) {
+ if (res < 0)
+ res = pos + 1;
+ else
+ return -1;
+
+ if (i + 1 < children.size())
+ continue;
+ }
+
+ if (res >= 0)
+ return next ? next->test(s, res) : res;
+ }
+ return -1;
+ }
+
+ void add_child(RegExNode* node) {
+
+ node->parent = this;
+ children.push_back(node);
+ }
+
+ void pop_back() {
+
+ memdelete(children[children.size() - 1]);
+ children.remove(children.size() - 1);
+ }
+};
+
+#define REGEX_EXPAND_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ return String();\
+}
+
+String RegExMatch::expand(const String& p_template) const {
+
+ String res;
+ for (const CharType* c = p_template.c_str(); *c != '\0'; ++c) {
+ if (c[0] == '\\') {
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_EXPAND_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ res += get_string(ref);
+
+ } else if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_EXPAND_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ res += get_string(name);
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_EXPAND_FAIL("invalid escape token");
+ res += String(&ch, 1);
+ c = d;
+ }
+ } else {
+ res += String(c, 1);
+ }
+ }
+ return res;
+}
+
+int RegExMatch::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegExMatch::get_group_array() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::INT)
+ continue;
+
+ if (capture.start >= 0)
+ res.push_back(string.substr(capture.start, capture.length));
+ else
+ res.push_back(String());
+ }
+ return res;
+}
+
+Array RegExMatch::get_names() const {
+
+ Array res;
+ for (int i = 1; i < captures.size(); ++i)
+ if (captures[i].name.get_type() == Variant::STRING)
+ res.push_back(captures[i].name);
+ return res;
+}
+
+Dictionary RegExMatch::get_name_dict() const {
+
+ Dictionary res;
+ for (int i = 1; i < captures.size(); ++i) {
+ const RegExMatch::Group& capture = captures[i];
+ if (capture.name.get_type() != Variant::STRING)
+ continue;
+
+ if (capture.start >= 0)
+ res[capture.name] = string.substr(capture.start, capture.length);
+ else
+ res[capture.name] = String();
+ }
+ return res;
+}
+
+String RegExMatch::get_string(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i) {
+
+ const RegExMatch::Group& capture = captures[i];
+
+ if (capture.name != p_name)
+ continue;
+
+ if (capture.start == -1)
+ return String();
+
+ return string.substr(capture.start, capture.length);
+ }
+ return String();
+}
+
+int RegExMatch::get_start(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start;
+ return -1;
+}
+
+int RegExMatch::get_end(const Variant& p_name) const {
+
+ for (int i = 0; i < captures.size(); ++i)
+ if (captures[i].name == p_name)
+ return captures[i].start + captures[i].length;
+ return -1;
+}
+
+RegExMatch::RegExMatch() {
+
+}
+
+static bool RegEx_is_shorthand(CharType ch) {
+
+ switch (ch) {
+ case 'w':
+ case 'W':
+ case 'd':
+ case 'D':
+ case 's':
+ case 'S':
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+#define REGEX_COMPILE_FAIL(MSG)\
+{\
+ ERR_PRINT(MSG);\
+ clear();\
+ return FAILED;\
+}
+
+Error RegEx::compile(const String& p_pattern) {
+
+ ERR_FAIL_COND_V(p_pattern.length() == 0, FAILED);
+
+ if (pattern == p_pattern && root)
+ return OK;
+
+ clear();
+ pattern = p_pattern;
+ group_names.push_back(0);
+ RegExNodeGroup* root_group = memnew(RegExNodeCapturing(0));
+ root = root_group;
+ Vector<RegExNodeGroup*> stack;
+ stack.push_back(root_group);
+ int lookahead_level = 0;
+ int numeric_groups = 0;
+ const int numeric_max = 9;
+
+ for (const CharType* c = p_pattern.c_str(); *c != '\0'; ++c) {
+
+ switch (c[0]) {
+ case '(':
+ if (c[1] == '?') {
+
+ RegExNodeGroup* group = NULL;
+ switch (c[2]) {
+ case ':':
+ c = &c[2];
+ group = memnew(RegExNodeGroup());
+ break;
+ case '!':
+ case '=':
+ group = memnew(RegExNodeLookAhead((c[2] == '!'), lookahead_level++));
+ if (lookahead_depth < lookahead_level)
+ lookahead_depth = lookahead_level;
+ c = &c[2];
+ break;
+ case '<':
+ if (c[3] == '!' || c[3] == '=') {
+ group = memnew(RegExNodeLookBehind((c[3] == '!'), lookahead_level++));
+ c = &c[3];
+ }
+ break;
+ case 'P':
+ if (c[3] == '<') {
+ const CharType* d = &c[3];
+ Variant name = RegExNodeCapturing::parse_name(d, false);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+ group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(name);
+ c = d;
+ }
+ default:
+ break;
+ }
+ if (!group)
+ REGEX_COMPILE_FAIL("unrecognised qualifier for group");
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else if (numeric_groups < numeric_max) {
+
+ RegExNodeCapturing* group = memnew(RegExNodeCapturing(group_names.size()));
+ group_names.push_back(++numeric_groups);
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+
+ } else {
+
+ RegExNodeGroup* group = memnew(RegExNodeGroup());
+ stack[0]->add_child(group);
+ stack.insert(0, group);
+ }
+ break;
+ case ')':
+ if (stack.size() == 1)
+ REGEX_COMPILE_FAIL("unexpected ')'");
+ stack.remove(0);
+ break;
+ case '\\':
+ if (('1' <= c[1] && c[1] <= '9') || (c[1] == 'g' && c[2] == '{')) {
+
+ int ref = 0;
+ bool unclosed = false;
+
+ if (c[1] == 'g') {
+ unclosed = true;
+ c = &c[2];
+ }
+
+ while ('0' <= c[1] && c[1] <= '9') {
+ ref = ref * 10 + int(c[1] - '0');
+ ++c;
+ }
+
+ if (unclosed) {
+ if (c[1] != '}')
+ REGEX_COMPILE_FAIL("unclosed backreference '{'");
+ ++c;
+ }
+
+ if (ref > numeric_groups || ref <= 0)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } if (c[1] =='g' && c[2] == '<') {
+
+ const CharType* d = &c[2];
+
+ Variant name = RegExNodeCapturing::parse_name(d, true);
+ if (name == Variant(-1))
+ REGEX_COMPILE_FAIL("unrecognised character for group name");
+
+ c = d;
+
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("backreferences inside lookbehind not supported");
+
+ int ref = -1;
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ if (group_names[i].get_type() == Variant::INT && int(group_names[i]) == ref) {
+ ref = group_names[i];
+ break;
+ }
+ }
+
+ if (ref == -1)
+ REGEX_COMPILE_FAIL("backreference not found");
+
+ stack[0]->add_child(memnew(RegExNodeBackReference(ref)));
+
+ } else if (c[1] == 'b' || c[1] == 'B') {
+
+ stack[0]->add_child(memnew(RegExNodeWordBoundary(*(++c) == 'B')));
+
+ } else if (RegEx_is_shorthand(c[1])) {
+
+ stack[0]->add_child(memnew(RegExNodeShorthand(*(++c))));
+
+ } else {
+
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ stack[0]->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+
+ }
+ break;
+ case '[':
+ {
+ RegExNodeBracket* bracket = memnew(RegExNodeBracket());
+ stack[0]->add_child(bracket);
+ if (c[1] == '^') {
+ bracket->inverse = true;
+ ++c;
+ }
+ bool first_child = true;
+ CharType previous_child;
+ bool previous_child_single = false;
+ while (true) {
+ ++c;
+ if (!first_child && c[0] == ']') {
+
+ break;
+
+ } else if (c[0] == '\0') {
+
+ REGEX_COMPILE_FAIL("unclosed bracket expression '['");
+
+ } else if (c[0] == '\\') {
+
+ if (RegEx_is_shorthand(c[1])) {
+ bracket->add_child(memnew(RegExNodeShorthand(*(++c))));
+ } else {
+ const CharType* d = c;
+ CharType ch = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ bracket->add_child(memnew(RegExNodeChar(ch)));
+ c = d;
+ previous_child = ch;
+ previous_child_single = true;
+ }
+
+ } else if (c[0] == ']' && c[1] == ':') {
+
+ const CharType* d = &c[2];
+ RegExNodeClass::Type type = RegExNodeClass::parse_type(d);
+ if (type != RegExNodeClass::Type_none) {
+
+ c = d;
+ previous_child_single = false;
+
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('[')));
+ previous_child = '[';
+ previous_child_single = true;
+ }
+ } else if (previous_child_single && c[0] == '-') {
+
+ if (c[1] != '\0' && c[1] != ']') {
+
+ CharType next;
+
+ if (c[1] == '\\') {
+ const CharType* d = ++c;
+ next = RegExNodeChar::parse_escape(d);
+ if (c == d)
+ REGEX_COMPILE_FAIL("invalid escape token");
+ } else {
+ next = *(++c);
+ }
+
+ if (next < previous_child)
+ REGEX_COMPILE_FAIL("text range out of order");
+
+ bracket->pop_back();
+ bracket->add_child(memnew(RegExNodeRange(previous_child, next)));
+ previous_child_single = false;
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar('-')));
+ previous_child = '-';
+ previous_child_single = true;
+ }
+ } else {
+
+ bracket->add_child(memnew(RegExNodeChar(c[0])));
+ previous_child = c[0];
+ previous_child_single = true;
+ }
+ first_child = false;
+ }
+ }
+ break;
+ case '|':
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("alternations inside lookbehind not supported");
+ stack[0]->add_childset();
+ break;
+ case '^':
+ stack[0]->add_child(memnew(RegExNodeAnchorStart()));
+ break;
+ case '$':
+ stack[0]->add_child(memnew(RegExNodeAnchorEnd()));
+ break;
+ case '.':
+ stack[0]->add_child(memnew(RegExNodeShorthand('.')));
+ break;
+ case '?':
+ case '*':
+ case '+':
+ case '{':
+ {
+ int min_val = 0;
+ int max_val = -1;
+ bool valid = true;
+ const CharType* d = c;
+ bool max_set = true;
+ switch (c[0]) {
+ case '?':
+ min_val = 0;
+ max_val = 1;
+ break;
+ case '*':
+ min_val = 0;
+ max_val = -1;
+ break;
+ case '+':
+ min_val = 1;
+ max_val = -1;
+ break;
+ case '{':
+ max_set = false;
+ while (valid) {
+ ++d;
+ if (d[0] == '}') {
+ break;
+ } else if (d[0] == ',') {
+ max_set = true;
+ } else if ('0' <= d[0] && d[0] <= '9') {
+ if (max_set) {
+ if (max_val < 0)
+ max_val = int(d[0] - '0');
+ else
+ max_val = max_val * 10 + int(d[0] - '0');
+ } else {
+ min_val = min_val * 10 + int(d[0] - '0');
+ }
+ } else {
+ valid = false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!max_set)
+ max_val = min_val;
+
+ if (valid) {
+
+ c = d;
+
+ if (stack[0]->back == NULL || !stack[0]->back->quantifiable)
+ REGEX_COMPILE_FAIL("element not quantifiable");
+
+ if (min_val != max_val)
+ for (int i = 0; i < stack.size(); ++i)
+ if (dynamic_cast<RegExNodeLookBehind*>(stack[i]))
+ REGEX_COMPILE_FAIL("variable length quantifiers inside lookbehind not supported");
+
+ RegExNodeQuantifier* quant = memnew(RegExNodeQuantifier(min_val, max_val));
+ quant->child = stack[0]->swap_back(quant);
+ quant->child->previous = NULL;
+ quant->child->parent = quant;
+
+ if (min_val == max_val && quant->child->length >= 0)
+ quant->length = max_val * quant->child->length;
+
+ if (c[1] == '?') {
+ quant->greedy = false;
+ ++c;
+ }
+ break;
+ }
+ }
+ default:
+ stack[0]->add_child(memnew(RegExNodeChar(c[0])));
+ break;
+ }
+ }
+ if (stack.size() > 1)
+ REGEX_COMPILE_FAIL("unclosed group '('");
+ return OK;
+}
+
+Ref<RegExMatch> RegEx::search(const String& p_text, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), NULL);
+ ERR_FAIL_COND_V(p_start < 0, NULL);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end > p_text.length(), NULL);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, NULL);
+
+ Ref<RegExMatch> res = memnew(RegExMatch());
+
+ for (int i = 0; i < group_names.size(); ++i) {
+ RegExMatch::Group group;
+ group.name = group_names[i];
+ res->captures.push_back(group);
+ }
+
+ res->string = p_text;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ RegExSearch s(res, p_end, lookahead_depth);
+
+ for (int i = p_start; i <= s.end; ++i) {
+ for (int c = 0; c < group_names.size(); ++c) {
+ res->captures[c].start = -1;
+ res->captures[c].length = 0;
+ }
+ if (root->test(s, i) >= 0)
+ break;
+ }
+
+ if (res->captures[0].start >= 0)
+ return res;
+ return NULL;
+}
+
+String RegEx::sub(const String& p_text, const String& p_replacement, bool p_all, int p_start, int p_end) const {
+
+ ERR_FAIL_COND_V(!is_valid(), p_text);
+ ERR_FAIL_COND_V(p_start < 0, p_text);
+ ERR_FAIL_COND_V(p_start >= p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end > p_text.length(), p_text);
+ ERR_FAIL_COND_V(p_end != -1 && p_end < p_start, p_text);
+
+ String text = p_text;
+ int start = p_start;
+
+ if (p_end == -1)
+ p_end = p_text.length();
+
+ while (start < text.length() && (p_all || start == p_start)) {
+
+ Ref<RegExMatch> m = search(text, start, p_end);
+
+ RegExMatch::Group& s = m->captures[0];
+
+ if (s.start < 0)
+ break;
+
+ String res = text.substr(0, s.start) + m->expand(p_replacement);
+
+ start = res.length();
+
+ if (s.length == 0)
+ ++start;
+
+ int sub_end = s.start + s.length;
+ if (sub_end < text.length())
+ res += text.substr(sub_end, text.length() - sub_end);
+
+ p_end += res.length() - text.length();
+
+ text = res;
+ }
+ return text;
+}
+
+void RegEx::clear() {
+
+ if (root)
+ memdelete(root);
+
+ root = NULL;
+ group_names.clear();
+ lookahead_depth = 0;
+}
+
+bool RegEx::is_valid() const {
+
+ return (root != NULL);
+}
+
+String RegEx::get_pattern() const {
+
+ return pattern;
+}
+
+int RegEx::get_group_count() const {
+
+ int count = 0;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::INT)
+ ++count;
+ return count;
+}
+
+Array RegEx::get_names() const {
+
+ Array res;
+ for (int i = 1; i < group_names.size(); ++i)
+ if (group_names[i].get_type() == Variant::STRING)
+ res.push_back(group_names[i]);
+ return res;
+}
+
+RegEx::RegEx() {
+
+ root = NULL;
+ lookahead_depth = 0;
+}
+
+RegEx::RegEx(const String& p_pattern) {
+
+ root = NULL;
+ compile(p_pattern);
+}
+
+RegEx::~RegEx() {
+
+ if (root)
+ memdelete(root);
+}
+
+void RegExMatch::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("expand","template"),&RegExMatch::expand);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegExMatch::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_group_array"),&RegExMatch::get_group_array);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegExMatch::get_names);
+ ObjectTypeDB::bind_method(_MD("get_name_dict"),&RegExMatch::get_name_dict);
+ ObjectTypeDB::bind_method(_MD("get_string","name"),&RegExMatch::get_string, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_start","name"),&RegExMatch::get_start, DEFVAL(0));
+ ObjectTypeDB::bind_method(_MD("get_end","name"),&RegExMatch::get_end, DEFVAL(0));
+}
+
+void RegEx::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("clear"),&RegEx::clear);
+ ObjectTypeDB::bind_method(_MD("compile","pattern"),&RegEx::compile);
+ ObjectTypeDB::bind_method(_MD("search","text","start","end"),&RegEx::search, DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("sub","text","replacement","all","start","end"),&RegEx::sub, DEFVAL(false), DEFVAL(0), DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("is_valid"),&RegEx::is_valid);
+ ObjectTypeDB::bind_method(_MD("get_pattern"),&RegEx::get_pattern);
+ ObjectTypeDB::bind_method(_MD("get_group_count"),&RegEx::get_group_count);
+ ObjectTypeDB::bind_method(_MD("get_names"),&RegEx::get_names);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "pattern"), _SCS("compile"), _SCS("get_pattern"));
+}
+
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
new file mode 100644
index 0000000000..803aa72b3f
--- /dev/null
+++ b/modules/regex/regex.h
@@ -0,0 +1,114 @@
+/*************************************************************************/
+/* regex.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef REGEX_H
+#define REGEX_H
+
+#include "core/vector.h"
+#include "core/ustring.h"
+#include "core/dictionary.h"
+#include "core/reference.h"
+#include "core/resource.h"
+
+class RegExNode;
+
+class RegExMatch : public Reference {
+
+ OBJ_TYPE(RegExMatch, Reference);
+
+ struct Group {
+ Variant name;
+ int start;
+ int length;
+ };
+
+ Vector<Group> captures;
+ String string;
+
+ friend class RegEx;
+ friend class RegExSearch;
+ friend class RegExNodeCapturing;
+ friend class RegExNodeBackReference;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ String expand(const String& p_template) const;
+
+ int get_group_count() const;
+ Array get_group_array() const;
+
+ Array get_names() const;
+ Dictionary get_name_dict() const;
+
+ String get_string(const Variant& p_name) const;
+ int get_start(const Variant& p_name) const;
+ int get_end(const Variant& p_name) const;
+
+ RegExMatch();
+
+};
+
+class RegEx : public Resource {
+
+ OBJ_TYPE(RegEx, Resource);
+
+ RegExNode* root;
+ Vector<Variant> group_names;
+ String pattern;
+ int lookahead_depth;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void clear();
+ Error compile(const String& p_pattern);
+
+ Ref<RegExMatch> search(const String& p_text, int p_start = 0, int p_end = -1) const;
+ String sub(const String& p_text, const String& p_replacement, bool p_all = false, int p_start = 0, int p_end = -1) const;
+
+ bool is_valid() const;
+ String get_pattern() const;
+ int get_group_count() const;
+ Array get_names() const;
+
+ RegEx();
+ RegEx(const String& p_pattern);
+ ~RegEx();
+
+};
+
+#endif // REGEX_H
+
diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp
new file mode 100644
index 0000000000..050cf3efff
--- /dev/null
+++ b/modules/regex/register_types.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "register_types.h"
+#include "object_type_db.h"
+#include "regex.h"
+
+void register_regex_types() {
+
+ ObjectTypeDB::register_type<RegExMatch>();
+ ObjectTypeDB::register_type<RegEx>();
+}
+
+void unregister_regex_types() {
+
+}
+
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
new file mode 100644
index 0000000000..df3b508e14
--- /dev/null
+++ b/modules/regex/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_regex_types();
+void unregister_regex_types();
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
new file mode 100644
index 0000000000..3fdc587652
--- /dev/null
+++ b/modules/squish/SCsub
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_squish = env_modules.Clone()
+
+# Thirdparty source files
+if (env["squish"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/squish/"
+ thirdparty_sources = [
+ "alpha.cpp",
+ "clusterfit.cpp",
+ "colourblock.cpp",
+ "colourfit.cpp",
+ "colourset.cpp",
+ "maths.cpp",
+ "rangefit.cpp",
+ "singlecolourfit.cpp",
+ "squish.cpp",
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_squish.add_source_files(env.modules_sources, thirdparty_sources)
+ env_squish.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_squish.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/squish/config.py b/modules/squish/config.py
new file mode 100644
index 0000000000..d28d9c702e
--- /dev/null
+++ b/modules/squish/config.py
@@ -0,0 +1,10 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ # Tools only, disabled for non-tools
+ # TODO: Find a cleaner way to achieve that
+ if (env["tools"] == "no"):
+ env["module_squish_enabled"] = "no"
+ env.disabled_modules.append("squish")
diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_compress_squish.cpp
new file mode 100644
index 0000000000..ac7c935ceb
--- /dev/null
+++ b/modules/squish/image_compress_squish.cpp
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* image_compress_squish.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "image_compress_squish.h"
+
+#include "print_string.h"
+
+#include <squish.h>
+
+void image_compress_squish(Image *p_image) {
+
+ int w=p_image->get_width();
+ int h=p_image->get_height();
+
+ if (p_image->get_mipmaps() == 0) {
+ ERR_FAIL_COND( !w || w % 4 != 0);
+ ERR_FAIL_COND( !h || h % 4 != 0);
+ } else {
+ ERR_FAIL_COND( !w || w !=nearest_power_of_2(w) );
+ ERR_FAIL_COND( !h || h !=nearest_power_of_2(h) );
+ };
+
+ if (p_image->get_format()>=Image::FORMAT_BC1)
+ return; //do not compress, already compressed
+
+ int shift=0;
+ int squish_comp=squish::kColourRangeFit;
+ Image::Format target_format;
+
+ if (p_image->get_format()==Image::FORMAT_GRAYSCALE_ALPHA) {
+ //compressed normalmap
+ target_format = Image::FORMAT_BC3; squish_comp|=squish::kDxt5;;
+ } else if (p_image->detect_alpha()!=Image::ALPHA_NONE) {
+
+ target_format = Image::FORMAT_BC2; squish_comp|=squish::kDxt3;;
+ } else {
+ target_format = Image::FORMAT_BC1; shift=1; squish_comp|=squish::kDxt1;;
+ }
+
+ p_image->convert(Image::FORMAT_RGBA); //always expects rgba
+
+ int mm_count = p_image->get_mipmaps();
+
+ DVector<uint8_t> data;
+ int target_size = Image::get_image_data_size(w,h,target_format,mm_count);
+ data.resize(target_size);
+
+ DVector<uint8_t>::Read rb = p_image->get_data().read();
+ DVector<uint8_t>::Write wb = data.write();
+
+ int dst_ofs=0;
+
+ for(int i=0;i<=mm_count;i++) {
+
+ int src_ofs = p_image->get_mipmap_offset(i);
+ squish::CompressImage( &rb[src_ofs],w,h,&wb[dst_ofs],squish_comp);
+ dst_ofs+=(MAX(4,w)*MAX(4,h))>>shift;
+ w>>=1;
+ h>>=1;
+ }
+
+ rb = DVector<uint8_t>::Read();
+ wb = DVector<uint8_t>::Write();
+
+ p_image->create(p_image->get_width(),p_image->get_height(),p_image->get_mipmaps(),target_format,data);
+
+}
diff --git a/modules/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h
new file mode 100644
index 0000000000..19dd900674
--- /dev/null
+++ b/modules/squish/image_compress_squish.h
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* image_compress_squish.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef IMAGE_COMPRESS_SQUISH_H
+#define IMAGE_COMPRESS_SQUISH_H
+
+#include "image.h"
+
+void image_compress_squish(Image *p_image);
+
+#endif // IMAGE_COMPRESS_SQUISH_H
diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp
new file mode 100644
index 0000000000..9e9621eb64
--- /dev/null
+++ b/modules/squish/register_types.cpp
@@ -0,0 +1,42 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "image_compress_squish.h"
+
+void register_squish_types() {
+
+ Image::set_compress_bc_func(image_compress_squish);
+}
+
+void unregister_squish_types() {}
+
+#endif
diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h
new file mode 100644
index 0000000000..bbde6a44bf
--- /dev/null
+++ b/modules/squish/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifdef TOOLS_ENABLED
+void register_squish_types();
+void unregister_squish_types();
+#endif
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
new file mode 100644
index 0000000000..22c618fe8b
--- /dev/null
+++ b/modules/theora/SCsub
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_theora = env_modules.Clone()
+
+# Thirdparty source files
+if (env["libtheora"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/libtheora/"
+ thirdparty_sources = [
+ #"analyze.c",
+ #"apiwrapper.c",
+ "bitpack.c",
+ "cpu.c",
+ #"decapiwrapper.c",
+ "decinfo.c",
+ "decode.c",
+ "dequant.c",
+ #"encapiwrapper.c",
+ #"encfrag.c",
+ #"encinfo.c",
+ #"encode.c",
+ #"encoder_disabled.c",
+ #"enquant.c",
+ #"fdct.c",
+ "fragment.c",
+ "huffdec.c",
+ #"huffenc.c",
+ "idct.c",
+ "info.c",
+ "internal.c",
+ #"mathops.c",
+ #"mcenc.c",
+ "quant.c",
+ #"rate.c",
+ "state.c",
+ #"tokenize.c",
+ ]
+
+ thirdparty_sources_x86 = [
+ #"x86/mmxencfrag.c",
+ #"x86/mmxfdct.c",
+ "x86/mmxfrag.c",
+ "x86/mmxidct.c",
+ "x86/mmxstate.c",
+ #"x86/sse2fdct.c",
+ #"x86/x86enc.c",
+ "x86/x86state.c",
+ ]
+
+ thirdparty_sources_x86_vc = [
+ #"x86_vc/mmxencfrag.c",
+ #"x86_vc/mmxfdct.c",
+ "x86_vc/mmxfrag.c",
+ "x86_vc/mmxidct.c",
+ "x86_vc/mmxstate.c",
+ #"x86_vc/x86enc.c",
+ "x86_vc/x86state.c",
+ ]
+
+ if (env["x86_libtheora_opt_gcc"]):
+ thirdparty_sources += thirdparty_sources_x86
+
+ if (env["x86_libtheora_opt_vc"]):
+ thirdparty_sources += thirdparty_sources_x86_vc
+
+ if (env["x86_libtheora_opt_gcc"] or env["x86_libtheora_opt_vc"]):
+ env_theora.Append(CCFLAGS = ["-DOC_X86_ASM"])
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_theora.add_source_files(env.modules_sources, thirdparty_sources)
+ env_theora.Append(CPPPATH = [thirdparty_dir])
+
+ # also requires libogg and libvorbis
+ if (env["libogg"] != "system"): # builtin
+ env_theora.Append(CPPPATH = ["#thirdparty/libogg"])
+ if (env["libvorbis"] != "system"): # builtin
+ env_theora.Append(CPPPATH = ["#thirdparty/libvorbis"])
+
+# Godot source files
+env_theora.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/theora/config.py b/modules/theora/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/theora/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
new file mode 100644
index 0000000000..282b59b0ec
--- /dev/null
+++ b/modules/theora/register_types.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "video_stream_theora.h"
+
+static ResourceFormatLoaderVideoStreamTheora* theora_stream_loader = NULL;
+
+void register_theora_types() {
+
+ theora_stream_loader = memnew( ResourceFormatLoaderVideoStreamTheora );
+ ResourceLoader::add_resource_format_loader(theora_stream_loader);
+ ObjectTypeDB::register_type<VideoStreamTheora>();
+}
+
+void unregister_theora_types() {
+
+ memdelete( theora_stream_loader );
+}
diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h
new file mode 100644
index 0000000000..18bdbf0c4c
--- /dev/null
+++ b/modules/theora/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_theora_types();
+void unregister_theora_types();
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
new file mode 100644
index 0000000000..3ddfee3a1d
--- /dev/null
+++ b/modules/theora/video_stream_theora.cpp
@@ -0,0 +1,940 @@
+/*************************************************************************/
+/* video_stream_theora.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "video_stream_theora.h"
+
+#include "globals.h"
+#include "os/os.h"
+#include "yuv2rgb.h"
+
+int VideoStreamPlaybackTheora:: buffer_data() {
+
+ char *buffer=ogg_sync_buffer(&oy,4096);
+
+#ifdef THEORA_USE_THREAD_STREAMING
+
+ int read;
+
+ do {
+ thread_sem->post();
+ read = MIN(ring_buffer.data_left(),4096);
+ if (read) {
+ ring_buffer.read((uint8_t*)buffer,read);
+ ogg_sync_wrote(&oy,read);
+ } else {
+ OS::get_singleton()->delay_usec(100);
+ }
+
+ } while(read==0);
+
+ return read;
+
+#else
+
+ int bytes=file->get_buffer((uint8_t*)buffer, 4096);
+ ogg_sync_wrote(&oy,bytes);
+ return(bytes);
+
+#endif
+}
+
+int VideoStreamPlaybackTheora::queue_page(ogg_page *page){
+ if(theora_p) {
+ ogg_stream_pagein(&to,page);
+ if (to.e_o_s)
+ theora_eos=true;
+ }
+ if(vorbis_p) {
+ ogg_stream_pagein(&vo,page);
+ if (vo.e_o_s)
+ vorbis_eos=true;
+ }
+ return 0;
+}
+
+void VideoStreamPlaybackTheora::video_write(void){
+ th_ycbcr_buffer yuv;
+ th_decode_ycbcr_out(td,yuv);
+
+ /*
+ int y_offset, uv_offset;
+ y_offset=(ti.pic_x&~1)+yuv[0].stride*(ti.pic_y&~1);
+
+ {
+ int pixels = size.x * size.y;
+ frame_data.resize(pixels * 4);
+ DVector<uint8_t>::Write w = frame_data.write();
+ char* dst = (char*)w.ptr();
+ int p = 0;
+ for (int i=0; i<size.y; i++) {
+
+ char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ char *out = dst + (int)size.x * 4 * i;
+ for (int j=0;j<size.x;j++) {
+
+ dst[p++] = in_y[j];
+ dst[p++] = in_y[j];
+ dst[p++] = in_y[j];
+ dst[p++] = 255;
+ };
+ }
+ format = Image::FORMAT_RGBA;
+ }
+ // */
+
+ //*
+
+ int pitch = 4;
+ frame_data.resize(size.x * size.y * pitch);
+ {
+ DVector<uint8_t>::Write w = frame_data.write();
+ char* dst = (char*)w.ptr();
+
+ //uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y/2);
+
+ if (px_fmt == TH_PF_444) {
+
+ yuv444_2_rgb8888((uint8_t*)dst, (uint8_t*)yuv[0].data, (uint8_t*)yuv[1].data, (uint8_t*)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x<<2, 0);
+
+ } else if (px_fmt == TH_PF_422) {
+
+ yuv422_2_rgb8888((uint8_t*)dst, (uint8_t*)yuv[0].data, (uint8_t*)yuv[1].data, (uint8_t*)yuv[2].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x<<2, 0);
+
+ } else if (px_fmt == TH_PF_420) {
+
+ yuv420_2_rgb8888((uint8_t*)dst, (uint8_t*)yuv[0].data, (uint8_t*)yuv[2].data, (uint8_t*)yuv[1].data, size.x, size.y, yuv[0].stride, yuv[1].stride, size.x<<2, 0);
+ };
+
+ format = Image::FORMAT_RGBA;
+ }
+
+ Image img(size.x,size.y,0,Image::FORMAT_RGBA,frame_data); //zero copy image creation
+
+ texture->set_data(img); //zero copy send to visual server
+
+ /*
+
+ if (px_fmt == TH_PF_444) {
+
+ int pitch = 3;
+ frame_data.resize(size.x * size.y * pitch);
+ DVector<uint8_t>::Write w = frame_data.write();
+ char* dst = (char*)w.ptr();
+
+ for(int i=0;i<size.y;i++) {
+
+ char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ char *out = dst + (int)size.x * pitch * i;
+ char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*i;
+ char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*i;
+ for (int j=0;j<size.x;j++) {
+
+ out[j*3+0] = in_y[j];
+ out[j*3+1] = in_u[j];
+ out[j*3+2] = in_v[j];
+ };
+ }
+
+ format = Image::FORMAT_YUV_444;
+
+ } else {
+
+ int div;
+ if (px_fmt!=TH_PF_422) {
+ div = 2;
+ }
+
+ bool rgba = true;
+ if (rgba) {
+
+ int pitch = 4;
+ frame_data.resize(size.x * size.y * pitch);
+ DVector<uint8_t>::Write w = frame_data.write();
+ char* dst = (char*)w.ptr();
+
+ uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y / div);
+ for(int i=0;i<size.y;i++) {
+ char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*(i/div);
+ char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*(i/div);
+ uint8_t *out = (uint8_t*)dst + (int)size.x * pitch * i;
+ int ofs = 0;
+ for (int j=0;j<size.x;j++) {
+
+ uint8_t y, u, v;
+ y = in_y[j];
+ u = in_u[j/2];
+ v = in_v[j/2];
+
+ int32_t r = Math::fast_ftoi(1.164 * (y - 16) + 1.596 * (v - 128));
+ int32_t g = Math::fast_ftoi(1.164 * (y - 16) - 0.813 * (v - 128) - 0.391 * (u - 128));
+ int32_t b = Math::fast_ftoi(1.164 * (y - 16) + 2.018 * (u - 128));
+
+ out[ofs++] = CLAMP(r, 0, 255);
+ out[ofs++] = CLAMP(g, 0, 255);
+ out[ofs++] = CLAMP(b, 0, 255);
+ out[ofs++] = 255;
+ }
+ }
+
+ format = Image::FORMAT_RGBA;
+
+ } else {
+
+ int pitch = 2;
+ frame_data.resize(size.x * size.y * pitch);
+ DVector<uint8_t>::Write w = frame_data.write();
+ char* dst = (char*)w.ptr();
+
+ uv_offset=(ti.pic_x/2)+(yuv[1].stride)*(ti.pic_y / div);
+ for(int i=0;i<size.y;i++) {
+ char *in_y = (char *)yuv[0].data+y_offset+yuv[0].stride*i;
+ char *out = dst + (int)size.x * pitch * i;
+ for (int j=0;j<size.x;j++)
+ out[j*2] = in_y[j];
+ char *in_u = (char *)yuv[1].data+uv_offset+yuv[1].stride*(i/div);
+ char *in_v = (char *)yuv[2].data+uv_offset+yuv[2].stride*(i/div);
+ for (int j=0;j<(int)size.x>>1;j++) {
+ out[j*4+1] = in_u[j];
+ out[j*4+3] = in_v[j];
+ }
+ }
+
+ format = Image::FORMAT_YUV_422;
+ };
+ };
+ // */
+
+ frames_pending = 1;
+}
+
+void VideoStreamPlaybackTheora::clear() {
+
+ if (!file)
+ return;
+
+ if(vorbis_p){
+ ogg_stream_clear(&vo);
+ if (vorbis_p >= 3) {
+ vorbis_block_clear(&vb);
+ vorbis_dsp_clear(&vd);
+ };
+ vorbis_comment_clear(&vc);
+ vorbis_info_clear(&vi);
+ vorbis_p = 0;
+ }
+ if(theora_p){
+ ogg_stream_clear(&to);
+ th_decode_free(td);
+ th_comment_clear(&tc);
+ th_info_clear(&ti);
+ theora_p = 0;
+ }
+ ogg_sync_clear(&oy);
+
+#ifdef THEORA_USE_THREAD_STREAMING
+ thread_exit=true;
+ thread_sem->post(); //just in case
+ Thread::wait_to_finish(thread);
+ memdelete(thread);
+ thread=NULL;
+ ring_buffer.clear();
+#endif
+ //file_name = "";
+
+ theora_p = 0;
+ vorbis_p = 0;
+ videobuf_ready = 0;
+ frames_pending = 0;
+ videobuf_time = 0;
+ theora_eos=false;
+ vorbis_eos=false;
+
+ if (file) {
+ memdelete(file);
+ }
+ file=NULL;
+ playing = false;
+};
+
+void VideoStreamPlaybackTheora::set_file(const String& p_file) {
+
+ ERR_FAIL_COND(playing);
+ ogg_packet op;
+ th_setup_info *ts = NULL;
+
+ file_name = p_file;
+ if (file) {
+ memdelete(file);
+ }
+ file = FileAccess::open(p_file, FileAccess::READ);
+ ERR_FAIL_COND(!file);
+
+#ifdef THEORA_USE_THREAD_STREAMING
+ thread_exit=false;
+ thread_eof=false;
+ //pre-fill buffer
+ int to_read = ring_buffer.space_left();
+ int read = file->get_buffer(read_buffer.ptr(),to_read);
+ ring_buffer.write(read_buffer.ptr(),read);
+
+ thread=Thread::create(_streaming_thread,this);
+
+#endif
+
+ ogg_sync_init(&oy);
+
+ /* init supporting Vorbis structures needed in header parsing */
+ vorbis_info_init(&vi);
+ vorbis_comment_init(&vc);
+
+ /* init supporting Theora structures needed in header parsing */
+ th_comment_init(&tc);
+ th_info_init(&ti);
+
+ theora_eos=false;
+ vorbis_eos=false;
+
+ /* Ogg file open; parse the headers */
+ /* Only interested in Vorbis/Theora streams */
+ int stateflag = 0;
+
+ int audio_track_skip=audio_track;
+
+
+ while(!stateflag){
+ int ret=buffer_data();
+ if(ret==0)break;
+ while(ogg_sync_pageout(&oy,&og)>0){
+ ogg_stream_state test;
+
+ /* is this a mandated initial header? If not, stop parsing */
+ if(!ogg_page_bos(&og)){
+ /* don't leak the page; get it into the appropriate stream */
+ queue_page(&og);
+ stateflag=1;
+ break;
+ }
+
+ ogg_stream_init(&test,ogg_page_serialno(&og));
+ ogg_stream_pagein(&test,&og);
+ ogg_stream_packetout(&test,&op);
+
+
+ /* identify the codec: try theora */
+ if(!theora_p && th_decode_headerin(&ti,&tc,&ts,&op)>=0){
+ /* it is theora */
+ copymem(&to,&test,sizeof(test));
+ theora_p=1;
+ }else if(!vorbis_p && vorbis_synthesis_headerin(&vi,&vc,&op)>=0){
+
+
+ /* it is vorbis */
+ if (audio_track_skip) {
+ vorbis_info_clear(&vi);
+ vorbis_comment_clear(&vc);
+ ogg_stream_clear(&test);
+ vorbis_info_init(&vi);
+ vorbis_comment_init(&vc);
+
+ audio_track_skip--;
+ } else {
+ copymem(&vo,&test,sizeof(test));
+ vorbis_p=1;
+ }
+ }else{
+ /* whatever it is, we don't care about it */
+ ogg_stream_clear(&test);
+ }
+ }
+ /* fall through to non-bos page parsing */
+ }
+
+ /* we're expecting more header packets. */
+ while((theora_p && theora_p<3) || (vorbis_p && vorbis_p<3)){
+ int ret;
+
+ /* look for further theora headers */
+ while(theora_p && (theora_p<3) && (ret=ogg_stream_packetout(&to,&op))){
+ if(ret<0){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ clear();
+ return;
+ }
+ if(!th_decode_headerin(&ti,&tc,&ts,&op)){
+ fprintf(stderr,"Error parsing Theora stream headers; "
+ "corrupt stream?\n");
+ clear();
+ return;
+ }
+ theora_p++;
+ }
+
+ /* look for more vorbis header packets */
+ while(vorbis_p && (vorbis_p<3) && (ret=ogg_stream_packetout(&vo,&op))){
+ if(ret<0){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ clear();
+ return;
+ }
+ ret = vorbis_synthesis_headerin(&vi,&vc,&op);
+ if(ret){
+ fprintf(stderr,"Error parsing Vorbis stream headers; corrupt stream?\n");
+ clear();
+ return;
+ }
+ vorbis_p++;
+ if(vorbis_p==3)break;
+ }
+
+ /* The header pages/packets will arrive before anything else we
+ care about, or the stream is not obeying spec */
+
+ if(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og); /* demux into the appropriate stream */
+ }else{
+ int ret=buffer_data(); /* someone needs more data */
+ if(ret==0){
+ fprintf(stderr,"End of file while searching for codec headers.\n");
+ clear();
+ return;
+ }
+ }
+ }
+
+ /* and now we have it all. initialize decoders */
+ if(theora_p){
+ td=th_decode_alloc(&ti,ts);
+ printf("Ogg logical stream %lx is Theora %dx%d %.02f fps",
+ to.serialno,ti.pic_width,ti.pic_height,
+ (double)ti.fps_numerator/ti.fps_denominator);
+ px_fmt=ti.pixel_fmt;
+ switch(ti.pixel_fmt){
+ case TH_PF_420: printf(" 4:2:0 video\n"); break;
+ case TH_PF_422: printf(" 4:2:2 video\n"); break;
+ case TH_PF_444: printf(" 4:4:4 video\n"); break;
+ case TH_PF_RSVD:
+ default:
+ printf(" video\n (UNKNOWN Chroma sampling!)\n");
+ break;
+ }
+ if(ti.pic_width!=ti.frame_width || ti.pic_height!=ti.frame_height)
+ printf(" Frame content is %dx%d with offset (%d,%d).\n",
+ ti.frame_width, ti.frame_height, ti.pic_x, ti.pic_y);
+ th_decode_ctl(td,TH_DECCTL_GET_PPLEVEL_MAX,&pp_level_max,
+ sizeof(pp_level_max));
+ pp_level=pp_level_max;
+ pp_level=0;
+ th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,sizeof(pp_level));
+ pp_inc=0;
+
+ /*{
+ int arg = 0xffff;
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MBMODE,&arg,sizeof(arg));
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_MV,&arg,sizeof(arg));
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_QI,&arg,sizeof(arg));
+ arg=10;
+ th_decode_ctl(td,TH_DECCTL_SET_TELEMETRY_BITS,&arg,sizeof(arg));
+ }*/
+
+ int w;
+ int h;
+ w=(ti.pic_x+ti.frame_width+1&~1)-(ti.pic_x&~1);
+ h=(ti.pic_y+ti.frame_height+1&~1)-(ti.pic_y&~1);
+ size.x = w;
+ size.y = h;
+
+ texture->create(w,h,Image::FORMAT_RGBA,Texture::FLAG_FILTER|Texture::FLAG_VIDEO_SURFACE);
+
+ }else{
+ /* tear down the partial theora setup */
+ th_info_clear(&ti);
+ th_comment_clear(&tc);
+ }
+
+ th_setup_free(ts);
+
+ if(vorbis_p){
+ vorbis_synthesis_init(&vd,&vi);
+ vorbis_block_init(&vd,&vb);
+ fprintf(stderr,"Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.\n",
+ vo.serialno,vi.channels,vi.rate);
+ //_setup(vi.channels, vi.rate);
+
+ }else{
+ /* tear down the partial vorbis setup */
+ vorbis_info_clear(&vi);
+ vorbis_comment_clear(&vc);
+ }
+
+ playing = false;
+ buffering=true;
+ time=0;
+ audio_frames_wrote=0;
+
+
+};
+
+float VideoStreamPlaybackTheora::get_time() const {
+
+ //print_line("total: "+itos(get_total())+" todo: "+itos(get_todo()));
+ //return MAX(0,time-((get_total())/(float)vi.rate));
+ return time-AudioServer::get_singleton()->get_output_delay()-delay_compensation;//-((get_total())/(float)vi.rate);
+};
+
+Ref<Texture> VideoStreamPlaybackTheora::get_texture() {
+
+ return texture;
+}
+
+void VideoStreamPlaybackTheora::update(float p_delta) {
+
+ if (!file)
+ return;
+
+ if (!playing || paused) {
+ //printf("not playing\n");
+ return;
+ };
+
+
+
+#ifdef THEORA_USE_THREAD_STREAMING
+ thread_sem->post();
+#endif
+
+ //double ctime =AudioServer::get_singleton()->get_mix_time();
+
+ //print_line("play "+rtos(p_delta));
+ time+=p_delta;
+
+ if (videobuf_time>get_time()) {
+ return; //no new frames need to be produced
+ }
+
+ bool frame_done=false;
+ bool audio_done=!vorbis_p;
+
+ while (!frame_done || (!audio_done && !vorbis_eos)) {
+ //a frame needs to be produced
+
+ ogg_packet op;
+ bool no_theora=false;
+
+
+ while (vorbis_p) {
+ int ret;
+ float **pcm;
+
+ bool buffer_full=false;
+
+ /* if there's pending, decoded audio, grab it */
+ if ((ret=vorbis_synthesis_pcmout(&vd,&pcm))>0) {
+
+
+
+ const int AUXBUF_LEN=4096;
+ int to_read = ret;
+ int16_t aux_buffer[AUXBUF_LEN];
+
+ while(to_read) {
+
+ int m = MIN(AUXBUF_LEN/vi.channels,to_read);
+
+ int count = 0;
+
+ for(int j=0;j<m;j++){
+ for(int i=0;i<vi.channels;i++){
+
+ int val=Math::fast_ftoi(pcm[i][j]*32767.f);
+ if(val>32767)val=32767;
+ if(val<-32768)val=-32768;
+ aux_buffer[count++] = val;
+ }
+ }
+
+ if (mix_callback) {
+ int mixed = mix_callback(mix_udata,aux_buffer,m);
+ to_read-=mixed;
+ if (mixed!=m) { //could mix no more
+ buffer_full=true;
+ break;
+ }
+ } else {
+ to_read-=m; //just pretend we sent the audio
+ }
+
+
+ }
+
+
+ int tr = vorbis_synthesis_read(&vd, ret-to_read);
+
+
+ if (vd.granulepos>=0) {
+ // print_line("wrote: "+itos(audio_frames_wrote)+" gpos: "+itos(vd.granulepos));
+ }
+
+ //print_line("mix audio!");
+
+ audio_frames_wrote+=ret-to_read;
+
+ //print_line("AGP: "+itos(vd.granulepos)+" added "+itos(ret-to_read));
+
+
+ } else {
+
+ /* no pending audio; is there a pending packet to decode? */
+ if (ogg_stream_packetout(&vo,&op)>0){
+ if(vorbis_synthesis(&vb,&op)==0) { /* test for success! */
+ vorbis_synthesis_blockin(&vd,&vb);
+ }
+ } else { /* we need more data; break out to suck in another page */
+ //printf("need moar data\n");
+ break;
+ };
+ }
+
+
+ audio_done = videobuf_time < (audio_frames_wrote/float(vi.rate));
+
+ if (buffer_full)
+ break;
+ }
+
+ while(theora_p && !frame_done){
+ /* theora is one in, one out... */
+ if(ogg_stream_packetout(&to,&op)>0){
+
+
+ if(false && pp_inc){
+ pp_level+=pp_inc;
+ th_decode_ctl(td,TH_DECCTL_SET_PPLEVEL,&pp_level,
+ sizeof(pp_level));
+ pp_inc=0;
+ }
+ /*HACK: This should be set after a seek or a gap, but we might not have
+ a granulepos for the first packet (we only have them for the last
+ packet on a page), so we just set it as often as we get it.
+ To do this right, we should back-track from the last packet on the
+ page and compute the correct granulepos for the first packet after
+ a seek or a gap.*/
+ if(op.granulepos>=0){
+ th_decode_ctl(td,TH_DECCTL_SET_GRANPOS,&op.granulepos,
+ sizeof(op.granulepos));
+ }
+ ogg_int64_t videobuf_granulepos;
+ if(th_decode_packetin(td,&op,&videobuf_granulepos)==0){
+ videobuf_time=th_granule_time(td,videobuf_granulepos);
+
+ //printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
+
+ /* is it already too old to be useful? This is only actually
+ useful cosmetically after a SIGSTOP. Note that we have to
+ decode the frame even if we don't show it (for now) due to
+ keyframing. Soon enough libtheora will be able to deal
+ with non-keyframe seeks. */
+
+ if(videobuf_time>=get_time()) {
+ frame_done=true;
+ } else{
+ /*If we are too slow, reduce the pp level.*/
+ pp_inc=pp_level>0?-1:0;
+ }
+ } else {
+
+ }
+
+ } else {
+ no_theora=true;
+ break;
+ }
+ }
+
+
+ //print_line("no theora: "+itos(no_theora)+" theora eos: "+itos(theora_eos)+" frame done "+itos(frame_done));
+
+#ifdef THEORA_USE_THREAD_STREAMING
+ if (file && thread_eof && no_theora && theora_eos && ring_buffer.data_left()==0) {
+#else
+ if (file && /*!videobuf_ready && */ no_theora && theora_eos) {
+#endif
+ printf("video done, stopping\n");
+ stop();
+ return;
+ };
+ #if 0
+ if (!videobuf_ready || audio_todo > 0){
+ /* no data yet for somebody. Grab another page */
+
+ buffer_data();
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+ }
+ #else
+
+
+ if (!frame_done || !audio_done){
+ //what's the point of waiting for audio to grab a page?
+
+ buffer_data();
+ while(ogg_sync_pageout(&oy,&og)>0){
+ queue_page(&og);
+ }
+ }
+ #endif
+ /* If playback has begun, top audio buffer off immediately. */
+ //if(stateflag) audio_write_nonblocking();
+
+ /* are we at or past time for this video frame? */
+ if(videobuf_ready && videobuf_time<=get_time()){
+
+ //video_write();
+ //videobuf_ready=0;
+ } else {
+ //printf("frame at %f not ready (time %f), ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
+ }
+
+ float tdiff=videobuf_time-get_time();
+ /*If we have lots of extra time, increase the post-processing level.*/
+ if(tdiff>ti.fps_denominator*0.25/ti.fps_numerator){
+ pp_inc=pp_level<pp_level_max?1:0;
+ }
+ else if(tdiff<ti.fps_denominator*0.05/ti.fps_numerator){
+ pp_inc=pp_level>0?-1:0;
+ }
+
+ }
+
+ video_write();
+
+};
+
+
+void VideoStreamPlaybackTheora::play() {
+
+ if (!playing)
+ time=0;
+ else {
+ stop();
+ }
+
+ playing = true;
+ delay_compensation=Globals::get_singleton()->get("audio/video_delay_compensation_ms");
+ delay_compensation/=1000.0;
+
+
+};
+
+void VideoStreamPlaybackTheora::stop() {
+
+ if (playing) {
+
+ clear();
+ set_file(file_name); //reset
+ }
+ playing = false;
+ time=0;
+};
+
+bool VideoStreamPlaybackTheora::is_playing() const {
+
+ return playing;
+};
+
+void VideoStreamPlaybackTheora::set_paused(bool p_paused) {
+
+ paused=p_paused;
+ //pau = !p_paused;
+};
+
+bool VideoStreamPlaybackTheora::is_paused(bool p_paused) const {
+
+ return paused;
+};
+
+void VideoStreamPlaybackTheora::set_loop(bool p_enable) {
+
+};
+
+bool VideoStreamPlaybackTheora::has_loop() const {
+
+ return false;
+};
+
+float VideoStreamPlaybackTheora::get_length() const {
+
+ return 0;
+};
+
+String VideoStreamPlaybackTheora::get_stream_name() const {
+
+ return "";
+};
+
+int VideoStreamPlaybackTheora::get_loop_count() const {
+
+ return 0;
+};
+
+float VideoStreamPlaybackTheora::get_pos() const {
+
+ return get_time();
+};
+
+void VideoStreamPlaybackTheora::seek_pos(float p_time) {
+
+ // no
+};
+
+void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback,void *p_userdata) {
+
+ mix_callback=p_callback;
+ mix_udata=p_userdata;
+}
+
+int VideoStreamPlaybackTheora::get_channels() const{
+
+ return vi.channels;
+}
+
+void VideoStreamPlaybackTheora::set_audio_track(int p_idx) {
+
+ audio_track=p_idx;
+}
+
+int VideoStreamPlaybackTheora::get_mix_rate() const{
+
+ return vi.rate;
+}
+
+#ifdef THEORA_USE_THREAD_STREAMING
+
+
+void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
+
+ VideoStreamPlaybackTheora *vs=(VideoStreamPlaybackTheora*)ud;
+
+ while(!vs->thread_exit) {
+
+ //just fill back the buffer
+ if (!vs->thread_eof) {
+
+ int to_read = vs->ring_buffer.space_left();
+ if (to_read) {
+ int read = vs->file->get_buffer(vs->read_buffer.ptr(),to_read);
+ vs->ring_buffer.write(vs->read_buffer.ptr(),read);
+ vs->thread_eof=vs->file->eof_reached();
+ }
+
+
+ }
+
+ vs->thread_sem->wait();
+ }
+}
+
+#endif
+
+VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
+
+ file = NULL;
+ theora_p = 0;
+ vorbis_p = 0;
+ videobuf_ready = 0;
+ playing = false;
+ frames_pending = 0;
+ videobuf_time = 0;
+ paused=false;
+
+ buffering=false;
+ texture = Ref<ImageTexture>( memnew(ImageTexture ));
+ mix_callback=NULL;
+ mix_udata=NULL;
+ audio_track=0;
+ delay_compensation=0;
+ audio_frames_wrote=0;
+
+#ifdef THEORA_USE_THREAD_STREAMING
+ int rb_power = nearest_shift(RB_SIZE_KB*1024);
+ ring_buffer.resize(rb_power);
+ read_buffer.resize(RB_SIZE_KB*1024);
+ thread_sem=Semaphore::create();
+ thread=NULL;
+ thread_exit=false;
+ thread_eof=false;
+
+#endif
+};
+
+VideoStreamPlaybackTheora::~VideoStreamPlaybackTheora() {
+
+#ifdef THEORA_USE_THREAD_STREAMING
+
+ memdelete(thread_sem);
+#endif
+ clear();
+
+ if (file)
+ memdelete(file);
+
+
+};
+
+
+RES ResourceFormatLoaderVideoStreamTheora::load(const String &p_path,const String& p_original_path, Error *r_error) {
+ if (r_error)
+ *r_error=ERR_FILE_CANT_OPEN;
+
+ VideoStreamTheora *stream = memnew(VideoStreamTheora);
+ stream->set_file(p_path);
+
+ if (r_error)
+ *r_error=OK;
+
+ return Ref<VideoStreamTheora>(stream);
+}
+
+void ResourceFormatLoaderVideoStreamTheora::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ogm");
+ p_extensions->push_back("ogv");
+}
+bool ResourceFormatLoaderVideoStreamTheora::handles_type(const String& p_type) const {
+ return (p_type=="VideoStream" || p_type=="VideoStreamTheora");
+}
+
+String ResourceFormatLoaderVideoStreamTheora::get_resource_type(const String &p_path) const {
+
+ String exl=p_path.extension().to_lower();
+ if (exl=="ogm" || exl=="ogv")
+ return "VideoStreamTheora";
+ return "";
+}
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
new file mode 100644
index 0000000000..04a5c56ee5
--- /dev/null
+++ b/modules/theora/video_stream_theora.h
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* video_stream_theora.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef VIDEO_STREAM_THEORA_H
+#define VIDEO_STREAM_THEORA_H
+
+#include "io/resource_loader.h"
+#include "os/file_access.h"
+#include "os/thread.h"
+#include "os/semaphore.h"
+#include "ring_buffer.h"
+#include "scene/resources/video_stream.h"
+
+#include <theora/theoradec.h>
+#include <vorbis/codec.h>
+
+//#define THEORA_USE_THREAD_STREAMING
+
+class VideoStreamPlaybackTheora : public VideoStreamPlayback {
+
+ OBJ_TYPE(VideoStreamPlaybackTheora, VideoStreamPlayback);
+
+ enum {
+ MAX_FRAMES = 4,
+ };
+
+ //Image frames[MAX_FRAMES];
+ Image::Format format;
+ DVector<uint8_t> frame_data;
+ int frames_pending;
+ FileAccess* file;
+ String file_name;
+ int audio_frames_wrote;
+ Point2i size;
+
+ int buffer_data();
+ int queue_page(ogg_page *page);
+ void video_write(void);
+ float get_time() const;
+
+ bool theora_eos;
+ bool vorbis_eos;
+
+ ogg_sync_state oy;
+ ogg_page og;
+ ogg_stream_state vo;
+ ogg_stream_state to;
+ th_info ti;
+ th_comment tc;
+ th_dec_ctx *td;
+ vorbis_info vi;
+ vorbis_dsp_state vd;
+ vorbis_block vb;
+ vorbis_comment vc;
+ th_pixel_fmt px_fmt;
+ double videobuf_time;
+ int pp_inc;
+
+ int theora_p;
+ int vorbis_p;
+ int pp_level_max;
+ int pp_level;
+ int videobuf_ready;
+
+ bool playing;
+ bool buffering;
+
+ double last_update_time;
+ double time;
+ double delay_compensation;
+
+ Ref<ImageTexture> texture;
+
+ AudioMixCallback mix_callback;
+ void* mix_udata;
+ bool paused;
+
+#ifdef THEORA_USE_THREAD_STREAMING
+
+ enum {
+ RB_SIZE_KB=1024
+ };
+
+ RingBuffer<uint8_t> ring_buffer;
+ Vector<uint8_t> read_buffer;
+ bool thread_eof;
+ Semaphore *thread_sem;
+ Thread *thread;
+ volatile bool thread_exit;
+
+ static void _streaming_thread(void *ud);
+
+#endif
+
+
+ int audio_track;
+
+protected:
+
+ void clear();
+
+public:
+
+ virtual void play();
+ virtual void stop();
+ virtual bool is_playing() const;
+
+ virtual void set_paused(bool p_paused);
+ virtual bool is_paused(bool p_paused) const;
+
+ virtual void set_loop(bool p_enable);
+ virtual bool has_loop() const;
+
+ virtual float get_length() const;
+
+ virtual String get_stream_name() const;
+
+ virtual int get_loop_count() const;
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+
+ void set_file(const String& p_file);
+
+ virtual Ref<Texture> get_texture();
+ virtual void update(float p_delta);
+
+ virtual void set_mix_callback(AudioMixCallback p_callback,void *p_userdata);
+ virtual int get_channels() const;
+ virtual int get_mix_rate() const;
+
+ virtual void set_audio_track(int p_idx);
+
+ VideoStreamPlaybackTheora();
+ ~VideoStreamPlaybackTheora();
+};
+
+
+
+class VideoStreamTheora : public VideoStream {
+
+ OBJ_TYPE(VideoStreamTheora,VideoStream);
+
+ String file;
+ int audio_track;
+
+
+public:
+
+ Ref<VideoStreamPlayback> instance_playback() {
+ Ref<VideoStreamPlaybackTheora> pb = memnew( VideoStreamPlaybackTheora );
+ pb->set_audio_track(audio_track);
+ pb->set_file(file);
+ return pb;
+ }
+
+ void set_file(const String& p_file) { file=p_file; }
+ void set_audio_track(int p_track) { audio_track=p_track; }
+
+ VideoStreamTheora() { audio_track=0; }
+
+};
+
+class ResourceFormatLoaderVideoStreamTheora : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+
+};
+
+#endif
diff --git a/modules/theora/yuv2rgb.h b/modules/theora/yuv2rgb.h
new file mode 100644
index 0000000000..59101bd057
--- /dev/null
+++ b/modules/theora/yuv2rgb.h
@@ -0,0 +1,1121 @@
+#ifndef YUV2RGB_H
+#define YUV2RGB_H
+
+#include "typedefs.h"
+
+static const uint32_t tables[256*3] =
+{
+ /* y_table */
+ 0x7FFFFFEDU,
+ 0x7FFFFFEFU,
+ 0x7FFFFFF0U,
+ 0x7FFFFFF1U,
+ 0x7FFFFFF2U,
+ 0x7FFFFFF3U,
+ 0x7FFFFFF4U,
+ 0x7FFFFFF6U,
+ 0x7FFFFFF7U,
+ 0x7FFFFFF8U,
+ 0x7FFFFFF9U,
+ 0x7FFFFFFAU,
+ 0x7FFFFFFBU,
+ 0x7FFFFFFDU,
+ 0x7FFFFFFEU,
+ 0x7FFFFFFFU,
+ 0x80000000U,
+ 0x80400801U,
+ 0x80A01002U,
+ 0x80E01803U,
+ 0x81202805U,
+ 0x81803006U,
+ 0x81C03807U,
+ 0x82004008U,
+ 0x82604809U,
+ 0x82A0500AU,
+ 0x82E0600CU,
+ 0x8340680DU,
+ 0x8380700EU,
+ 0x83C0780FU,
+ 0x84208010U,
+ 0x84608811U,
+ 0x84A09813U,
+ 0x8500A014U,
+ 0x8540A815U,
+ 0x8580B016U,
+ 0x85E0B817U,
+ 0x8620C018U,
+ 0x8660D01AU,
+ 0x86C0D81BU,
+ 0x8700E01CU,
+ 0x8740E81DU,
+ 0x87A0F01EU,
+ 0x87E0F81FU,
+ 0x88210821U,
+ 0x88811022U,
+ 0x88C11823U,
+ 0x89012024U,
+ 0x89412825U,
+ 0x89A13026U,
+ 0x89E14028U,
+ 0x8A214829U,
+ 0x8A81502AU,
+ 0x8AC1582BU,
+ 0x8B01602CU,
+ 0x8B61682DU,
+ 0x8BA1782FU,
+ 0x8BE18030U,
+ 0x8C418831U,
+ 0x8C819032U,
+ 0x8CC19833U,
+ 0x8D21A034U,
+ 0x8D61B036U,
+ 0x8DA1B837U,
+ 0x8E01C038U,
+ 0x8E41C839U,
+ 0x8E81D03AU,
+ 0x8EE1D83BU,
+ 0x8F21E83DU,
+ 0x8F61F03EU,
+ 0x8FC1F83FU,
+ 0x90020040U,
+ 0x90420841U,
+ 0x90A21042U,
+ 0x90E22044U,
+ 0x91222845U,
+ 0x91823046U,
+ 0x91C23847U,
+ 0x92024048U,
+ 0x92624849U,
+ 0x92A2504AU,
+ 0x92E2604CU,
+ 0x9342684DU,
+ 0x9382704EU,
+ 0x93C2784FU,
+ 0x94228050U,
+ 0x94628851U,
+ 0x94A29853U,
+ 0x9502A054U,
+ 0x9542A855U,
+ 0x9582B056U,
+ 0x95E2B857U,
+ 0x9622C058U,
+ 0x9662D05AU,
+ 0x96C2D85BU,
+ 0x9702E05CU,
+ 0x9742E85DU,
+ 0x97A2F05EU,
+ 0x97E2F85FU,
+ 0x98230861U,
+ 0x98831062U,
+ 0x98C31863U,
+ 0x99032064U,
+ 0x99632865U,
+ 0x99A33066U,
+ 0x99E34068U,
+ 0x9A434869U,
+ 0x9A83506AU,
+ 0x9AC3586BU,
+ 0x9B23606CU,
+ 0x9B63686DU,
+ 0x9BA3786FU,
+ 0x9BE38070U,
+ 0x9C438871U,
+ 0x9C839072U,
+ 0x9CC39873U,
+ 0x9D23A074U,
+ 0x9D63B076U,
+ 0x9DA3B877U,
+ 0x9E03C078U,
+ 0x9E43C879U,
+ 0x9E83D07AU,
+ 0x9EE3D87BU,
+ 0x9F23E87DU,
+ 0x9F63F07EU,
+ 0x9FC3F87FU,
+ 0xA0040080U,
+ 0xA0440881U,
+ 0xA0A41082U,
+ 0xA0E42084U,
+ 0xA1242885U,
+ 0xA1843086U,
+ 0xA1C43887U,
+ 0xA2044088U,
+ 0xA2644889U,
+ 0xA2A4588BU,
+ 0xA2E4608CU,
+ 0xA344688DU,
+ 0xA384708EU,
+ 0xA3C4788FU,
+ 0xA4248090U,
+ 0xA4649092U,
+ 0xA4A49893U,
+ 0xA504A094U,
+ 0xA544A895U,
+ 0xA584B096U,
+ 0xA5E4B897U,
+ 0xA624C098U,
+ 0xA664D09AU,
+ 0xA6C4D89BU,
+ 0xA704E09CU,
+ 0xA744E89DU,
+ 0xA7A4F09EU,
+ 0xA7E4F89FU,
+ 0xA82508A1U,
+ 0xA88510A2U,
+ 0xA8C518A3U,
+ 0xA90520A4U,
+ 0xA96528A5U,
+ 0xA9A530A6U,
+ 0xA9E540A8U,
+ 0xAA4548A9U,
+ 0xAA8550AAU,
+ 0xAAC558ABU,
+ 0xAB2560ACU,
+ 0xAB6568ADU,
+ 0xABA578AFU,
+ 0xAC0580B0U,
+ 0xAC4588B1U,
+ 0xAC8590B2U,
+ 0xACE598B3U,
+ 0xAD25A0B4U,
+ 0xAD65B0B6U,
+ 0xADA5B8B7U,
+ 0xAE05C0B8U,
+ 0xAE45C8B9U,
+ 0xAE85D0BAU,
+ 0xAEE5D8BBU,
+ 0xAF25E8BDU,
+ 0xAF65F0BEU,
+ 0xAFC5F8BFU,
+ 0xB00600C0U,
+ 0xB04608C1U,
+ 0xB0A610C2U,
+ 0xB0E620C4U,
+ 0xB12628C5U,
+ 0xB18630C6U,
+ 0xB1C638C7U,
+ 0xB20640C8U,
+ 0xB26648C9U,
+ 0xB2A658CBU,
+ 0xB2E660CCU,
+ 0xB34668CDU,
+ 0xB38670CEU,
+ 0xB3C678CFU,
+ 0xB42680D0U,
+ 0xB46690D2U,
+ 0xB4A698D3U,
+ 0xB506A0D4U,
+ 0xB546A8D5U,
+ 0xB586B0D6U,
+ 0xB5E6B8D7U,
+ 0xB626C8D9U,
+ 0xB666D0DAU,
+ 0xB6C6D8DBU,
+ 0xB706E0DCU,
+ 0xB746E8DDU,
+ 0xB7A6F0DEU,
+ 0xB7E6F8DFU,
+ 0xB82708E1U,
+ 0xB88710E2U,
+ 0xB8C718E3U,
+ 0xB90720E4U,
+ 0xB96728E5U,
+ 0xB9A730E6U,
+ 0xB9E740E8U,
+ 0xBA4748E9U,
+ 0xBA8750EAU,
+ 0xBAC758EBU,
+ 0xBB2760ECU,
+ 0xBB6768EDU,
+ 0xBBA778EFU,
+ 0xBC0780F0U,
+ 0xBC4788F1U,
+ 0xBC8790F2U,
+ 0xBCE798F3U,
+ 0xBD27A0F4U,
+ 0xBD67B0F6U,
+ 0xBDC7B8F7U,
+ 0xBE07C0F8U,
+ 0xBE47C8F9U,
+ 0xBEA7D0FAU,
+ 0xBEE7D8FBU,
+ 0xBF27E8FDU,
+ 0xBF87F0FEU,
+ 0xBFC7F8FFU,
+ 0xC0080100U,
+ 0xC0480901U,
+ 0xC0A81102U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ 0xC0E82104U,
+ /* u_table */
+ 0x0C400103U,
+ 0x0C200105U,
+ 0x0C200107U,
+ 0x0C000109U,
+ 0x0BE0010BU,
+ 0x0BC0010DU,
+ 0x0BA0010FU,
+ 0x0BA00111U,
+ 0x0B800113U,
+ 0x0B600115U,
+ 0x0B400117U,
+ 0x0B400119U,
+ 0x0B20011BU,
+ 0x0B00011DU,
+ 0x0AE0011FU,
+ 0x0AE00121U,
+ 0x0AC00123U,
+ 0x0AA00125U,
+ 0x0A800127U,
+ 0x0A600129U,
+ 0x0A60012BU,
+ 0x0A40012DU,
+ 0x0A20012FU,
+ 0x0A000131U,
+ 0x0A000132U,
+ 0x09E00134U,
+ 0x09C00136U,
+ 0x09A00138U,
+ 0x09A0013AU,
+ 0x0980013CU,
+ 0x0960013EU,
+ 0x09400140U,
+ 0x09400142U,
+ 0x09200144U,
+ 0x09000146U,
+ 0x08E00148U,
+ 0x08C0014AU,
+ 0x08C0014CU,
+ 0x08A0014EU,
+ 0x08800150U,
+ 0x08600152U,
+ 0x08600154U,
+ 0x08400156U,
+ 0x08200158U,
+ 0x0800015AU,
+ 0x0800015CU,
+ 0x07E0015EU,
+ 0x07C00160U,
+ 0x07A00162U,
+ 0x07A00164U,
+ 0x07800166U,
+ 0x07600168U,
+ 0x0740016AU,
+ 0x0720016CU,
+ 0x0720016EU,
+ 0x07000170U,
+ 0x06E00172U,
+ 0x06C00174U,
+ 0x06C00176U,
+ 0x06A00178U,
+ 0x0680017AU,
+ 0x0660017CU,
+ 0x0660017EU,
+ 0x06400180U,
+ 0x06200182U,
+ 0x06000184U,
+ 0x05E00185U,
+ 0x05E00187U,
+ 0x05C00189U,
+ 0x05A0018BU,
+ 0x0580018DU,
+ 0x0580018FU,
+ 0x05600191U,
+ 0x05400193U,
+ 0x05200195U,
+ 0x05200197U,
+ 0x05000199U,
+ 0x04E0019BU,
+ 0x04C0019DU,
+ 0x04C0019FU,
+ 0x04A001A1U,
+ 0x048001A3U,
+ 0x046001A5U,
+ 0x044001A7U,
+ 0x044001A9U,
+ 0x042001ABU,
+ 0x040001ADU,
+ 0x03E001AFU,
+ 0x03E001B1U,
+ 0x03C001B3U,
+ 0x03A001B5U,
+ 0x038001B7U,
+ 0x038001B9U,
+ 0x036001BBU,
+ 0x034001BDU,
+ 0x032001BFU,
+ 0x032001C1U,
+ 0x030001C3U,
+ 0x02E001C5U,
+ 0x02C001C7U,
+ 0x02A001C9U,
+ 0x02A001CBU,
+ 0x028001CDU,
+ 0x026001CFU,
+ 0x024001D1U,
+ 0x024001D3U,
+ 0x022001D5U,
+ 0x020001D7U,
+ 0x01E001D8U,
+ 0x01E001DAU,
+ 0x01C001DCU,
+ 0x01A001DEU,
+ 0x018001E0U,
+ 0x016001E2U,
+ 0x016001E4U,
+ 0x014001E6U,
+ 0x012001E8U,
+ 0x010001EAU,
+ 0x010001ECU,
+ 0x00E001EEU,
+ 0x00C001F0U,
+ 0x00A001F2U,
+ 0x00A001F4U,
+ 0x008001F6U,
+ 0x006001F8U,
+ 0x004001FAU,
+ 0x004001FCU,
+ 0x002001FEU,
+ 0x00000200U,
+ 0xFFE00202U,
+ 0xFFC00204U,
+ 0xFFC00206U,
+ 0xFFA00208U,
+ 0xFF80020AU,
+ 0xFF60020CU,
+ 0xFF60020EU,
+ 0xFF400210U,
+ 0xFF200212U,
+ 0xFF000214U,
+ 0xFF000216U,
+ 0xFEE00218U,
+ 0xFEC0021AU,
+ 0xFEA0021CU,
+ 0xFEA0021EU,
+ 0xFE800220U,
+ 0xFE600222U,
+ 0xFE400224U,
+ 0xFE200226U,
+ 0xFE200228U,
+ 0xFE000229U,
+ 0xFDE0022BU,
+ 0xFDC0022DU,
+ 0xFDC0022FU,
+ 0xFDA00231U,
+ 0xFD800233U,
+ 0xFD600235U,
+ 0xFD600237U,
+ 0xFD400239U,
+ 0xFD20023BU,
+ 0xFD00023DU,
+ 0xFCE0023FU,
+ 0xFCE00241U,
+ 0xFCC00243U,
+ 0xFCA00245U,
+ 0xFC800247U,
+ 0xFC800249U,
+ 0xFC60024BU,
+ 0xFC40024DU,
+ 0xFC20024FU,
+ 0xFC200251U,
+ 0xFC000253U,
+ 0xFBE00255U,
+ 0xFBC00257U,
+ 0xFBC00259U,
+ 0xFBA0025BU,
+ 0xFB80025DU,
+ 0xFB60025FU,
+ 0xFB400261U,
+ 0xFB400263U,
+ 0xFB200265U,
+ 0xFB000267U,
+ 0xFAE00269U,
+ 0xFAE0026BU,
+ 0xFAC0026DU,
+ 0xFAA0026FU,
+ 0xFA800271U,
+ 0xFA800273U,
+ 0xFA600275U,
+ 0xFA400277U,
+ 0xFA200279U,
+ 0xFA20027BU,
+ 0xFA00027CU,
+ 0xF9E0027EU,
+ 0xF9C00280U,
+ 0xF9A00282U,
+ 0xF9A00284U,
+ 0xF9800286U,
+ 0xF9600288U,
+ 0xF940028AU,
+ 0xF940028CU,
+ 0xF920028EU,
+ 0xF9000290U,
+ 0xF8E00292U,
+ 0xF8E00294U,
+ 0xF8C00296U,
+ 0xF8A00298U,
+ 0xF880029AU,
+ 0xF860029CU,
+ 0xF860029EU,
+ 0xF84002A0U,
+ 0xF82002A2U,
+ 0xF80002A4U,
+ 0xF80002A6U,
+ 0xF7E002A8U,
+ 0xF7C002AAU,
+ 0xF7A002ACU,
+ 0xF7A002AEU,
+ 0xF78002B0U,
+ 0xF76002B2U,
+ 0xF74002B4U,
+ 0xF74002B6U,
+ 0xF72002B8U,
+ 0xF70002BAU,
+ 0xF6E002BCU,
+ 0xF6C002BEU,
+ 0xF6C002C0U,
+ 0xF6A002C2U,
+ 0xF68002C4U,
+ 0xF66002C6U,
+ 0xF66002C8U,
+ 0xF64002CAU,
+ 0xF62002CCU,
+ 0xF60002CEU,
+ 0xF60002CFU,
+ 0xF5E002D1U,
+ 0xF5C002D3U,
+ 0xF5A002D5U,
+ 0xF5A002D7U,
+ 0xF58002D9U,
+ 0xF56002DBU,
+ 0xF54002DDU,
+ 0xF52002DFU,
+ 0xF52002E1U,
+ 0xF50002E3U,
+ 0xF4E002E5U,
+ 0xF4C002E7U,
+ 0xF4C002E9U,
+ 0xF4A002EBU,
+ 0xF48002EDU,
+ 0xF46002EFU,
+ 0xF46002F1U,
+ 0xF44002F3U,
+ 0xF42002F5U,
+ 0xF40002F7U,
+ 0xF3E002F9U,
+ 0xF3E002FBU,
+ /* v_table */
+ 0x1A09A000U,
+ 0x19E9A800U,
+ 0x19A9B800U,
+ 0x1969C800U,
+ 0x1949D000U,
+ 0x1909E000U,
+ 0x18C9E800U,
+ 0x18A9F800U,
+ 0x186A0000U,
+ 0x182A1000U,
+ 0x180A2000U,
+ 0x17CA2800U,
+ 0x17AA3800U,
+ 0x176A4000U,
+ 0x172A5000U,
+ 0x170A6000U,
+ 0x16CA6800U,
+ 0x168A7800U,
+ 0x166A8000U,
+ 0x162A9000U,
+ 0x160AA000U,
+ 0x15CAA800U,
+ 0x158AB800U,
+ 0x156AC000U,
+ 0x152AD000U,
+ 0x14EAE000U,
+ 0x14CAE800U,
+ 0x148AF800U,
+ 0x146B0000U,
+ 0x142B1000U,
+ 0x13EB2000U,
+ 0x13CB2800U,
+ 0x138B3800U,
+ 0x134B4000U,
+ 0x132B5000U,
+ 0x12EB6000U,
+ 0x12CB6800U,
+ 0x128B7800U,
+ 0x124B8000U,
+ 0x122B9000U,
+ 0x11EBA000U,
+ 0x11ABA800U,
+ 0x118BB800U,
+ 0x114BC000U,
+ 0x112BD000U,
+ 0x10EBE000U,
+ 0x10ABE800U,
+ 0x108BF800U,
+ 0x104C0000U,
+ 0x100C1000U,
+ 0x0FEC2000U,
+ 0x0FAC2800U,
+ 0x0F8C3800U,
+ 0x0F4C4000U,
+ 0x0F0C5000U,
+ 0x0EEC5800U,
+ 0x0EAC6800U,
+ 0x0E6C7800U,
+ 0x0E4C8000U,
+ 0x0E0C9000U,
+ 0x0DEC9800U,
+ 0x0DACA800U,
+ 0x0D6CB800U,
+ 0x0D4CC000U,
+ 0x0D0CD000U,
+ 0x0CCCD800U,
+ 0x0CACE800U,
+ 0x0C6CF800U,
+ 0x0C4D0000U,
+ 0x0C0D1000U,
+ 0x0BCD1800U,
+ 0x0BAD2800U,
+ 0x0B6D3800U,
+ 0x0B2D4000U,
+ 0x0B0D5000U,
+ 0x0ACD5800U,
+ 0x0AAD6800U,
+ 0x0A6D7800U,
+ 0x0A2D8000U,
+ 0x0A0D9000U,
+ 0x09CD9800U,
+ 0x098DA800U,
+ 0x096DB800U,
+ 0x092DC000U,
+ 0x090DD000U,
+ 0x08CDD800U,
+ 0x088DE800U,
+ 0x086DF800U,
+ 0x082E0000U,
+ 0x07EE1000U,
+ 0x07CE1800U,
+ 0x078E2800U,
+ 0x076E3800U,
+ 0x072E4000U,
+ 0x06EE5000U,
+ 0x06CE5800U,
+ 0x068E6800U,
+ 0x064E7800U,
+ 0x062E8000U,
+ 0x05EE9000U,
+ 0x05CE9800U,
+ 0x058EA800U,
+ 0x054EB800U,
+ 0x052EC000U,
+ 0x04EED000U,
+ 0x04AED800U,
+ 0x048EE800U,
+ 0x044EF000U,
+ 0x042F0000U,
+ 0x03EF1000U,
+ 0x03AF1800U,
+ 0x038F2800U,
+ 0x034F3000U,
+ 0x030F4000U,
+ 0x02EF5000U,
+ 0x02AF5800U,
+ 0x028F6800U,
+ 0x024F7000U,
+ 0x020F8000U,
+ 0x01EF9000U,
+ 0x01AF9800U,
+ 0x016FA800U,
+ 0x014FB000U,
+ 0x010FC000U,
+ 0x00EFD000U,
+ 0x00AFD800U,
+ 0x006FE800U,
+ 0x004FF000U,
+ 0x00100000U,
+ 0xFFD01000U,
+ 0xFFB01800U,
+ 0xFF702800U,
+ 0xFF303000U,
+ 0xFF104000U,
+ 0xFED05000U,
+ 0xFEB05800U,
+ 0xFE706800U,
+ 0xFE307000U,
+ 0xFE108000U,
+ 0xFDD09000U,
+ 0xFD909800U,
+ 0xFD70A800U,
+ 0xFD30B000U,
+ 0xFD10C000U,
+ 0xFCD0D000U,
+ 0xFC90D800U,
+ 0xFC70E800U,
+ 0xFC30F000U,
+ 0xFBF10000U,
+ 0xFBD11000U,
+ 0xFB911800U,
+ 0xFB712800U,
+ 0xFB313000U,
+ 0xFAF14000U,
+ 0xFAD14800U,
+ 0xFA915800U,
+ 0xFA516800U,
+ 0xFA317000U,
+ 0xF9F18000U,
+ 0xF9D18800U,
+ 0xF9919800U,
+ 0xF951A800U,
+ 0xF931B000U,
+ 0xF8F1C000U,
+ 0xF8B1C800U,
+ 0xF891D800U,
+ 0xF851E800U,
+ 0xF831F000U,
+ 0xF7F20000U,
+ 0xF7B20800U,
+ 0xF7921800U,
+ 0xF7522800U,
+ 0xF7123000U,
+ 0xF6F24000U,
+ 0xF6B24800U,
+ 0xF6925800U,
+ 0xF6526800U,
+ 0xF6127000U,
+ 0xF5F28000U,
+ 0xF5B28800U,
+ 0xF5729800U,
+ 0xF552A800U,
+ 0xF512B000U,
+ 0xF4F2C000U,
+ 0xF4B2C800U,
+ 0xF472D800U,
+ 0xF452E800U,
+ 0xF412F000U,
+ 0xF3D30000U,
+ 0xF3B30800U,
+ 0xF3731800U,
+ 0xF3532800U,
+ 0xF3133000U,
+ 0xF2D34000U,
+ 0xF2B34800U,
+ 0xF2735800U,
+ 0xF2336800U,
+ 0xF2137000U,
+ 0xF1D38000U,
+ 0xF1B38800U,
+ 0xF1739800U,
+ 0xF133A800U,
+ 0xF113B000U,
+ 0xF0D3C000U,
+ 0xF093C800U,
+ 0xF073D800U,
+ 0xF033E000U,
+ 0xF013F000U,
+ 0xEFD40000U,
+ 0xEF940800U,
+ 0xEF741800U,
+ 0xEF342000U,
+ 0xEEF43000U,
+ 0xEED44000U,
+ 0xEE944800U,
+ 0xEE745800U,
+ 0xEE346000U,
+ 0xEDF47000U,
+ 0xEDD48000U,
+ 0xED948800U,
+ 0xED549800U,
+ 0xED34A000U,
+ 0xECF4B000U,
+ 0xECD4C000U,
+ 0xEC94C800U,
+ 0xEC54D800U,
+ 0xEC34E000U,
+ 0xEBF4F000U,
+ 0xEBB50000U,
+ 0xEB950800U,
+ 0xEB551800U,
+ 0xEB352000U,
+ 0xEAF53000U,
+ 0xEAB54000U,
+ 0xEA954800U,
+ 0xEA555800U,
+ 0xEA156000U,
+ 0xE9F57000U,
+ 0xE9B58000U,
+ 0xE9958800U,
+ 0xE9559800U,
+ 0xE915A000U,
+ 0xE8F5B000U,
+ 0xE8B5C000U,
+ 0xE875C800U,
+ 0xE855D800U,
+ 0xE815E000U,
+ 0xE7F5F000U,
+ 0xE7B60000U,
+ 0xE7760800U,
+ 0xE7561800U,
+ 0xE7162000U,
+ 0xE6D63000U,
+ 0xE6B64000U,
+ 0xE6764800U,
+ 0xE6365800U
+};
+
+#define FLAGS 0x40080100
+#define READUV(U,V) (tables[256 + (U)] + tables[512 + (V)])
+#define READY(Y) tables[Y]
+#define FIXUP(Y) \
+do { \
+ int tmp = (Y) & FLAGS; \
+ if (tmp != 0) \
+ { \
+ tmp -= tmp>>8; \
+ (Y) |= tmp; \
+ tmp = FLAGS & ~(Y>>1); \
+ (Y) += tmp>>8; \
+ } \
+} while (0 == 1)
+
+#define STORE(Y,DSTPTR) \
+do { \
+ *(DSTPTR)++ = (Y); \
+ *(DSTPTR)++ = (Y)>>22; \
+ *(DSTPTR)++ = (Y)>>11; \
+ *(DSTPTR)++ = 255; \
+} while (0 == 1)
+
+void yuv422_2_rgb8888(uint8_t *dst_ptr,
+ const uint8_t *y_ptr,
+ const uint8_t *u_ptr,
+ const uint8_t *v_ptr,
+ int32_t width,
+ int32_t height,
+ int32_t y_span,
+ int32_t uv_span,
+ int32_t dst_span,
+ int32_t dither)
+{
+ height -= 1;
+ while (height > 0)
+ {
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do top row pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ y1 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ FIXUP(y1);
+ STORE(y0, dst_ptr);
+ STORE(y1, dst_ptr);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing top row pix */
+ uint32_t uv, y0;
+
+ uv = READUV(*u_ptr,*v_ptr);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ }
+ dst_ptr += dst_span-width*4;
+ y_ptr += y_span-width;
+ u_ptr += uv_span-(width>>1);
+ v_ptr += uv_span-(width>>1);
+ height = (height<<16)>>16;
+ height -= 1;
+ if (height == 0)
+ break;
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do second row pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ y1 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ FIXUP(y1);
+ STORE(y0, dst_ptr);
+ STORE(y1, dst_ptr);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing bottom row pix */
+ uint32_t uv, y0;
+
+ uv = READUV(*u_ptr,*v_ptr);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ }
+ dst_ptr += dst_span-width*4;
+ y_ptr += y_span-width;
+ u_ptr += uv_span-(width>>1);
+ v_ptr += uv_span-(width>>1);
+ height = (height<<16)>>16;
+ height -= 1;
+ }
+}
+
+
+#undef FLAGS
+#undef READUV
+#undef READY
+#undef FIXUP
+#undef STORE
+
+
+#define FLAGS 0x40080100
+#define READUV(U,V) (tables[256 + (U)] + tables[512 + (V)])
+#define READY(Y) tables[Y]
+#define FIXUP(Y) \
+do { \
+ int tmp = (Y) & FLAGS; \
+ if (tmp != 0) \
+ { \
+ tmp -= tmp>>8; \
+ (Y) |= tmp; \
+ tmp = FLAGS & ~(Y>>1); \
+ (Y) += tmp>>8; \
+ } \
+} while (0 == 1)
+
+#define STORE(Y,DSTPTR) \
+do { \
+ (DSTPTR) = 0xFF000000 | (Y & 0xFF) | (0xFF00 & (Y>>14)) | (0xFF0000 & (Y<<5));\
+} while (0 == 1)
+
+void yuv420_2_rgb8888(uint8_t *dst_ptr_,
+ const uint8_t *y_ptr,
+ const uint8_t *u_ptr,
+ const uint8_t *v_ptr,
+ int32_t width,
+ int32_t height,
+ int32_t y_span,
+ int32_t uv_span,
+ int32_t dst_span,
+ int32_t dither)
+{
+ uint32_t *dst_ptr = (uint32_t *)(void *)dst_ptr_;
+ dst_span >>= 2;
+
+ height -= 1;
+ while (height > 0)
+ {
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do 2 column pairs */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y1 = uv + READY(y_ptr[y_span]);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ FIXUP(y0);
+ STORE(y1, dst_ptr[dst_span]);
+ STORE(y0, *dst_ptr++);
+ y1 = uv + READY(y_ptr[y_span]);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ FIXUP(y0);
+ STORE(y1, dst_ptr[dst_span]);
+ STORE(y0, *dst_ptr++);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing column pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr,*v_ptr);
+ y1 = uv + READY(y_ptr[y_span]);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ FIXUP(y0);
+ STORE(y0, dst_ptr[dst_span]);
+ STORE(y1, *dst_ptr++);
+ }
+ dst_ptr += dst_span*2-width;
+ y_ptr += y_span*2-width;
+ u_ptr += uv_span-(width>>1);
+ v_ptr += uv_span-(width>>1);
+ height = (height<<16)>>16;
+ height -= 2;
+ }
+ if (height == 0)
+ {
+ /* Trail row */
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do a row pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y1 = uv + READY(*y_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ FIXUP(y0);
+ STORE(y1, *dst_ptr++);
+ STORE(y0, *dst_ptr++);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing pix */
+ uint32_t uv, y0;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, *dst_ptr++);
+ }
+ }
+}
+
+
+
+#undef FLAGS
+#undef READUV
+#undef READY
+#undef FIXUP
+#undef STORE
+
+#define FLAGS 0x40080100
+#define READUV(U,V) (tables[256 + (U)] + tables[512 + (V)])
+#define READY(Y) tables[Y]
+#define FIXUP(Y) \
+do { \
+ int tmp = (Y) & FLAGS; \
+ if (tmp != 0) \
+ { \
+ tmp -= tmp>>8; \
+ (Y) |= tmp; \
+ tmp = FLAGS & ~(Y>>1); \
+ (Y) += tmp>>8; \
+ } \
+} while (0 == 1)
+
+#define STORE(Y,DSTPTR) \
+do { \
+ *(DSTPTR)++ = (Y); \
+ *(DSTPTR)++ = (Y)>>22; \
+ *(DSTPTR)++ = (Y)>>11; \
+ *(DSTPTR)++ = 255; \
+} while (0 == 1)
+
+void yuv444_2_rgb8888(uint8_t *dst_ptr,
+ const uint8_t *y_ptr,
+ const uint8_t *u_ptr,
+ const uint8_t *v_ptr,
+ int32_t width,
+ int32_t height,
+ int32_t y_span,
+ int32_t uv_span,
+ int32_t dst_span,
+ int32_t dither)
+{
+ height -= 1;
+ while (height > 0)
+ {
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do top row pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y1 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ STORE(y1, dst_ptr);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing top row pix */
+ uint32_t uv, y0;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ }
+ dst_ptr += dst_span-width*4;
+ y_ptr += y_span-width;
+ u_ptr += uv_span-width;
+ v_ptr += uv_span-width;
+ height = (height<<16)>>16;
+ height -= 1;
+ if (height == 0)
+ break;
+ height -= width<<16;
+ height += 1<<16;
+ while (height < 0)
+ {
+ /* Do second row pair */
+ uint32_t uv, y0, y1;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y1 = uv + READY(*y_ptr++);
+ FIXUP(y1);
+ STORE(y1, dst_ptr);
+ height += (2<<16);
+ }
+ if ((height>>16) == 0)
+ {
+ /* Trailing bottom row pix */
+ uint32_t uv, y0;
+
+ uv = READUV(*u_ptr++,*v_ptr++);
+ y0 = uv + READY(*y_ptr++);
+ FIXUP(y0);
+ STORE(y0, dst_ptr);
+ }
+ dst_ptr += dst_span-width*4;
+ y_ptr += y_span-width;
+ u_ptr += uv_span-width;
+ v_ptr += uv_span-width;
+ height = (height<<16)>>16;
+ height -= 1;
+ }
+}
+#endif // YUV2RGB_H
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
index 403fe68f66..0882406761 100644
--- a/modules/visual_script/SCsub
+++ b/modules/visual_script/SCsub
@@ -1,5 +1,7 @@
+#!/usr/bin/env python
+
Import('env')
-env.add_source_files(env.modules_sources,"*.cpp")
+env.add_source_files(env.modules_sources, "*.cpp")
Export('env')
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 24a44d3506..2bfb6bc9ea 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -55,6 +55,7 @@ const char* VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"convert",
"typeof",
"type_exists",
+ "char",
"str",
"print",
"printerr",
@@ -141,6 +142,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case LOGIC_NEAREST_PO2:
case OBJ_WEAKREF:
case TYPE_OF:
+ case TEXT_CHAR:
case TEXT_STR:
case TEXT_PRINT:
case TEXT_PRINTERR:
@@ -362,6 +364,12 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::STRING,"type");
} break;
+ case TEXT_CHAR: {
+
+ return PropertyInfo(Variant::INT,"ascii");
+
+
+ } break;
case TEXT_STR: {
return PropertyInfo(Variant::NIL,"value");
@@ -517,6 +525,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t=Variant::BOOL;
} break;
+ case TEXT_CHAR:
case TEXT_STR: {
t=Variant::STRING;
@@ -975,6 +984,13 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func,const Variant** p_inp
*r_return = ObjectTypeDB::type_exists(*p_inputs[0]);
} break;
+ case VisualScriptBuiltinFunc::TEXT_CHAR: {
+
+ CharType result[2] = {*p_inputs[0], 0};
+
+ *r_return=String(result);
+
+ } break;
case VisualScriptBuiltinFunc::TEXT_STR: {
String str = *p_inputs[0];
@@ -1213,6 +1229,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists",create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/char",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_CHAR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/str",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_STR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/print",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr",create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index 000230d84f..8b0d70c1ea 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -57,6 +57,7 @@ public:
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
+ TEXT_CHAR,
TEXT_STR,
TEXT_PRINT,
TEXT_PRINTERR,
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
new file mode 100644
index 0000000000..3220cb454c
--- /dev/null
+++ b/modules/vorbis/SCsub
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_vorbis = env_modules.Clone()
+
+# Thirdparty source files
+if (env["libvorbis"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/libvorbis/"
+ thirdparty_sources = [
+ #"analysis.c",
+ #"barkmel.c",
+ "bitrate.c",
+ "block.c",
+ "codebook.c",
+ "envelope.c",
+ "floor0.c",
+ "floor1.c",
+ "info.c",
+ "lookup.c",
+ "lpc.c",
+ "lsp.c",
+ "mapping0.c",
+ "mdct.c",
+ "psy.c",
+ #"psytune.c",
+ "registry.c",
+ "res0.c",
+ "sharedbook.c",
+ "smallft.c",
+ "synthesis.c",
+ #"tone.c",
+ #"vorbisenc.c",
+ "vorbisfile.c",
+ "window.c",
+ ]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_vorbis.add_source_files(env.modules_sources, thirdparty_sources)
+ env_vorbis.Append(CPPPATH = [thirdparty_dir])
+
+ # also requires libogg
+ if (env["libogg"] != "system"): # builtin
+ env_vorbis.Append(CPPPATH = ["#thirdparty/libogg"])
+
+# Godot source files
+env_vorbis.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
new file mode 100644
index 0000000000..4ce7940a01
--- /dev/null
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -0,0 +1,430 @@
+/*************************************************************************/
+/* audio_stream_ogg_vorbis.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "audio_stream_ogg_vorbis.h"
+
+
+
+size_t AudioStreamPlaybackOGGVorbis::_ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f) {
+
+ //printf("read to %p, %i bytes, %i nmemb, %p\n",p_dst,p_data,p_count,_f);
+ FileAccess *fa=(FileAccess*)_f;
+ size_t read_total = p_data*p_count;
+
+ if (fa->eof_reached())
+ return 0;
+
+ uint8_t *dst=(uint8_t*)p_dst;
+
+ int read = fa->get_buffer(dst, read_total);
+
+ return read;
+}
+
+int AudioStreamPlaybackOGGVorbis::_ov_seek_func(void *_f,ogg_int64_t offs, int whence) {
+
+ //printf("seek to %p, offs %i, whence %i\n",_f,(int)offs,whence);
+
+#ifdef SEEK_SET
+ //printf("seek set defined\n");
+ FileAccess *fa=(FileAccess*)_f;
+
+ if (whence==SEEK_SET) {
+
+ fa->seek(offs);
+ } else if (whence==SEEK_CUR) {
+
+ fa->seek(fa->get_pos()+offs);
+ } else if (whence==SEEK_END) {
+
+ fa->seek_end(offs);
+ } else {
+
+ ERR_PRINT("BUG, wtf was whence set to?\n");
+ }
+ int ret=fa->eof_reached()?-1:0;
+ //printf("returning %i\n",ret);
+ return ret;
+
+#else
+ return -1; // no seeking
+#endif
+
+}
+int AudioStreamPlaybackOGGVorbis::_ov_close_func(void *_f) {
+
+// printf("close %p\n",_f);
+ if (!_f)
+ return 0;
+ FileAccess *fa=(FileAccess*)_f;
+ if (fa->is_open())
+ fa->close();
+ return 0;
+}
+long AudioStreamPlaybackOGGVorbis::_ov_tell_func(void *_f) {
+
+ //printf("close %p\n",_f);
+
+ FileAccess *fa=(FileAccess*)_f;
+ return fa->get_pos();
+}
+
+
+
+int AudioStreamPlaybackOGGVorbis::mix(int16_t* p_bufer,int p_frames) {
+
+ if (!playing)
+ return 0;
+
+ int total=p_frames;
+ while (true) {
+
+ int todo = p_frames;
+
+ if (todo==0 || todo<MIN_MIX) {
+ break;
+ }
+
+ //printf("to mix %i - mix me %i bytes\n",to_mix,to_mix*stream_channels*sizeof(int16_t));
+
+ #ifdef BIG_ENDIAN_ENABLED
+ long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 1, 2, 1, &current_section);
+ #else
+ long ret=ov_read(&vf,(char*)p_bufer,todo*stream_channels*sizeof(int16_t), 0, 2, 1, &current_section);
+ #endif
+
+ if (ret<0) {
+
+ playing = false;
+ ERR_EXPLAIN("Error reading OGG Vorbis File: "+file);
+ ERR_BREAK(ret<0);
+ } else if (ret==0) { // end of song, reload?
+
+ ov_clear(&vf);
+
+ _close_file();
+
+ if (!has_loop()) {
+
+ playing=false;
+ repeats=1;
+ break;
+ }
+
+ f=FileAccess::open(file,FileAccess::READ);
+
+ int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
+ if (errv!=0) {
+ playing=false;
+ break;; // :(
+ }
+
+ if (loop_restart_time) {
+ bool ok = ov_time_seek(&vf,loop_restart_time)==0;
+ if (!ok) {
+ playing=false;
+ //ERR_EXPLAIN("loop restart time rejected");
+ ERR_PRINT("loop restart time rejected")
+ }
+
+ frames_mixed=stream_srate*loop_restart_time;
+ } else {
+
+ frames_mixed=0;
+ }
+ repeats++;
+ continue;
+
+ }
+
+ ret/=stream_channels;
+ ret/=sizeof(int16_t);
+
+ frames_mixed+=ret;
+
+ p_bufer+=ret*stream_channels;
+ p_frames-=ret;
+
+ }
+
+ return total-p_frames;
+
+}
+
+
+
+void AudioStreamPlaybackOGGVorbis::play(float p_from) {
+
+ if (playing)
+ stop();
+
+ if (_load_stream()!=OK)
+ return;
+
+
+ frames_mixed=0;
+ playing=true;
+ if (p_from>0) {
+ seek_pos(p_from);
+ }
+}
+
+void AudioStreamPlaybackOGGVorbis::_close_file() {
+
+ if (f) {
+
+ memdelete(f);
+ f=NULL;
+ }
+}
+
+bool AudioStreamPlaybackOGGVorbis::is_playing() const {
+ return playing;
+}
+void AudioStreamPlaybackOGGVorbis::stop() {
+
+ _clear_stream();
+ playing=false;
+ //_clear();
+}
+
+
+
+float AudioStreamPlaybackOGGVorbis::get_pos() const {
+
+ int32_t frames = int32_t(frames_mixed);
+ if (frames < 0)
+ frames=0;
+ return double(frames) / stream_srate;
+}
+
+void AudioStreamPlaybackOGGVorbis::seek_pos(float p_time) {
+
+
+
+ if (!playing)
+ return;
+ bool ok = ov_time_seek(&vf,p_time)==0;
+ ERR_FAIL_COND(!ok);
+ frames_mixed=stream_srate*p_time;
+}
+
+String AudioStreamPlaybackOGGVorbis::get_stream_name() const {
+
+ return "";
+}
+
+void AudioStreamPlaybackOGGVorbis::set_loop(bool p_enable) {
+
+ loops=p_enable;
+}
+
+bool AudioStreamPlaybackOGGVorbis::has_loop() const {
+
+ return loops;
+}
+
+int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
+ return repeats;
+}
+
+
+Error AudioStreamPlaybackOGGVorbis::set_file(const String& p_file) {
+
+ file=p_file;
+ stream_valid=false;
+ Error err;
+ f=FileAccess::open(file,FileAccess::READ,&err);
+
+ if (err) {
+ ERR_FAIL_COND_V( err, err );
+ }
+
+ int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
+ switch(errv) {
+
+ case OV_EREAD: { // - A read from media returned an error.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CANT_READ );
+ } break;
+ case OV_EVERSION: // - Vorbis version mismatch.
+ case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ } break;
+ case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CORRUPT );
+ } break;
+ case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_BUG );
+ } break;
+ }
+ const vorbis_info *vinfo=ov_info(&vf,-1);
+ stream_channels=vinfo->channels;
+ stream_srate=vinfo->rate;
+ length = ov_time_total(&vf,-1);
+ ov_clear(&vf);
+ memdelete(f);
+ f=NULL;
+ stream_valid=true;
+
+
+ return OK;
+}
+
+Error AudioStreamPlaybackOGGVorbis::_load_stream() {
+
+ ERR_FAIL_COND_V(!stream_valid,ERR_UNCONFIGURED);
+
+ _clear_stream();
+ if (file=="")
+ return ERR_INVALID_DATA;
+
+ Error err;
+ f=FileAccess::open(file,FileAccess::READ,&err);
+ if (err) {
+ ERR_FAIL_COND_V( err, err );
+ }
+
+ int errv = ov_open_callbacks(f,&vf,NULL,0,_ov_callbacks);
+ switch(errv) {
+
+ case OV_EREAD: { // - A read from media returned an error.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CANT_READ );
+ } break;
+ case OV_EVERSION: // - Vorbis version mismatch.
+ case OV_ENOTVORBIS: { // - Bitstream is not Vorbis data.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_UNRECOGNIZED );
+ } break;
+ case OV_EBADHEADER: { // - Invalid Vorbis bitstream header.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_FILE_CORRUPT );
+ } break;
+ case OV_EFAULT: { // - Internal logic fault; indicates a bug or heap/stack corruption.
+ memdelete(f); f=NULL;
+ ERR_FAIL_V( ERR_BUG );
+ } break;
+ }
+ repeats=0;
+ stream_loaded=true;
+
+
+ return OK;
+}
+
+
+float AudioStreamPlaybackOGGVorbis::get_length() const {
+
+ if (!stream_loaded) {
+ if (const_cast<AudioStreamPlaybackOGGVorbis*>(this)->_load_stream()!=OK)
+ return 0;
+ }
+ return length;
+}
+
+void AudioStreamPlaybackOGGVorbis::_clear_stream() {
+
+ if (!stream_loaded)
+ return;
+
+ ov_clear(&vf);
+ _close_file();
+
+ stream_loaded=false;
+ //stream_channels=1;
+ playing=false;
+}
+
+void AudioStreamPlaybackOGGVorbis::set_paused(bool p_paused) {
+
+ paused=p_paused;
+}
+
+bool AudioStreamPlaybackOGGVorbis::is_paused(bool p_paused) const {
+
+ return paused;
+}
+
+
+AudioStreamPlaybackOGGVorbis::AudioStreamPlaybackOGGVorbis() {
+
+ loops=false;
+ playing=false;
+ _ov_callbacks.read_func=_ov_read_func;
+ _ov_callbacks.seek_func=_ov_seek_func;
+ _ov_callbacks.close_func=_ov_close_func;
+ _ov_callbacks.tell_func=_ov_tell_func;
+ f = NULL;
+ stream_loaded=false;
+ stream_valid=false;
+ repeats=0;
+ paused=true;
+ stream_channels=0;
+ stream_srate=0;
+ current_section=0;
+ length=0;
+ loop_restart_time=0;
+}
+
+
+AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
+
+ _clear_stream();
+
+}
+
+
+
+RES ResourceFormatLoaderAudioStreamOGGVorbis::load(const String &p_path, const String& p_original_path, Error *r_error) {
+ if (r_error)
+ *r_error=OK;
+
+ AudioStreamOGGVorbis *ogg_stream = memnew(AudioStreamOGGVorbis);
+ ogg_stream->set_file(p_path);
+ return Ref<AudioStreamOGGVorbis>(ogg_stream);
+}
+
+void ResourceFormatLoaderAudioStreamOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("ogg");
+}
+String ResourceFormatLoaderAudioStreamOGGVorbis::get_resource_type(const String &p_path) const {
+
+ if (p_path.extension().to_lower()=="ogg")
+ return "AudioStreamOGGVorbis";
+ return "";
+}
+
+bool ResourceFormatLoaderAudioStreamOGGVorbis::handles_type(const String& p_type) const {
+ return (p_type=="AudioStream" || p_type=="AudioStreamOGG" || p_type=="AudioStreamOGGVorbis");
+}
+
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
new file mode 100644
index 0000000000..8d8d7392b5
--- /dev/null
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -0,0 +1,142 @@
+/*************************************************************************/
+/* audio_stream_ogg_vorbis.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef AUDIO_STREAM_OGG_VORBIS_H
+#define AUDIO_STREAM_OGG_VORBIS_H
+
+#include "io/resource_loader.h"
+#include "os/file_access.h"
+#include "os/thread_safe.h"
+#include "scene/resources/audio_stream.h"
+
+#include <vorbis/vorbisfile.h>
+
+class AudioStreamPlaybackOGGVorbis : public AudioStreamPlayback {
+
+ OBJ_TYPE(AudioStreamPlaybackOGGVorbis,AudioStreamPlayback);
+
+ enum {
+ MIN_MIX=1024
+ };
+
+ FileAccess *f;
+
+ ov_callbacks _ov_callbacks;
+ float length;
+ static size_t _ov_read_func(void *p_dst,size_t p_data, size_t p_count, void *_f);
+ static int _ov_seek_func(void *_f,ogg_int64_t offs, int whence);
+ static int _ov_close_func(void *_f);
+ static long _ov_tell_func(void *_f);
+
+ String file;
+ int64_t frames_mixed;
+
+ bool stream_loaded;
+ volatile bool playing;
+ OggVorbis_File vf;
+ int stream_channels;
+ int stream_srate;
+ int current_section;
+
+
+ bool paused;
+ bool loops;
+ int repeats;
+
+ Error _load_stream();
+ void _clear_stream();
+ void _close_file();
+
+ bool stream_valid;
+ float loop_restart_time;
+
+
+public:
+
+
+ Error set_file(const String& p_file);
+
+ virtual void play(float p_from=0);
+ virtual void stop();
+ virtual bool is_playing() const;
+
+ virtual void set_loop_restart_time(float p_time) { loop_restart_time=p_time; }
+
+ virtual void set_paused(bool p_paused);
+ virtual bool is_paused(bool p_paused) const;
+
+ virtual void set_loop(bool p_enable);
+ virtual bool has_loop() const;
+
+ virtual float get_length() const;
+
+ virtual String get_stream_name() const;
+
+ virtual int get_loop_count() const;
+
+ virtual float get_pos() const;
+ virtual void seek_pos(float p_time);
+
+ virtual int get_channels() const { return stream_channels; }
+ virtual int get_mix_rate() const { return stream_srate; }
+
+ virtual int get_minimum_buffer_size() const { return 0; }
+ virtual int mix(int16_t* p_bufer,int p_frames);
+
+ AudioStreamPlaybackOGGVorbis();
+ ~AudioStreamPlaybackOGGVorbis();
+};
+
+
+class AudioStreamOGGVorbis : public AudioStream {
+
+ OBJ_TYPE(AudioStreamOGGVorbis,AudioStream);
+
+ String file;
+public:
+
+ Ref<AudioStreamPlayback> instance_playback() {
+ Ref<AudioStreamPlaybackOGGVorbis> pb = memnew( AudioStreamPlaybackOGGVorbis );
+ pb->set_file(file);
+ return pb;
+ }
+
+ void set_file(const String& p_file) { file=p_file; }
+
+};
+
+class ResourceFormatLoaderAudioStreamOGGVorbis : public ResourceFormatLoader {
+public:
+ virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String& p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+
+#endif // AUDIO_STREAM_OGG_H
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/vorbis/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
new file mode 100644
index 0000000000..ae63b5af3c
--- /dev/null
+++ b/modules/vorbis/register_types.cpp
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "audio_stream_ogg_vorbis.h"
+
+static ResourceFormatLoaderAudioStreamOGGVorbis *vorbis_stream_loader = NULL;
+
+void register_vorbis_types() {
+
+ vorbis_stream_loader = memnew( ResourceFormatLoaderAudioStreamOGGVorbis );
+ ResourceLoader::add_resource_format_loader(vorbis_stream_loader);
+ ObjectTypeDB::register_type<AudioStreamOGGVorbis>();
+}
+
+void unregister_vorbis_types() {
+
+ memdelete( vorbis_stream_loader );
+}
diff --git a/modules/vorbis/register_types.h b/modules/vorbis/register_types.h
new file mode 100644
index 0000000000..6baaed7ce8
--- /dev/null
+++ b/modules/vorbis/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_vorbis_types();
+void unregister_vorbis_types();
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
new file mode 100644
index 0000000000..38585a1ff2
--- /dev/null
+++ b/modules/webp/SCsub
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_webp = env_modules.Clone()
+
+# Thirdparty source files
+if (env["libwebp"] != "system"): # builtin
+ thirdparty_dir = "#thirdparty/libwebp/"
+ thirdparty_sources = [
+ "enc/webpenc.c",
+ "enc/near_lossless.c",
+ "enc/frame.c",
+ "enc/alpha.c",
+ "enc/picture_csp.c",
+ "enc/vp8l.c",
+ "enc/picture_psnr.c",
+ "enc/delta_palettization.c",
+ "enc/syntax.c",
+ "enc/backward_references.c",
+ "enc/token.c",
+ "enc/analysis.c",
+ "enc/iterator.c",
+ "enc/picture_tools.c",
+ "enc/picture_rescale.c",
+ "enc/config.c",
+ "enc/tree.c",
+ "enc/cost.c",
+ "enc/picture.c",
+ "enc/quant.c",
+ "enc/filter.c",
+ "enc/histogram.c",
+ "utils/rescaler.c",
+ "utils/filters.c",
+ "utils/quant_levels_dec.c",
+ "utils/huffman.c",
+ "utils/thread.c",
+ "utils/quant_levels.c",
+ "utils/bit_writer.c",
+ "utils/bit_reader.c",
+ "utils/random.c",
+ "utils/utils.c",
+ "utils/huffman_encode.c",
+ "utils/color_cache.c",
+ "mux/muxinternal.c",
+ "mux/muxread.c",
+ "mux/anim_encode.c",
+ "mux/muxedit.c",
+ "dec/webp.c",
+ "dec/frame.c",
+ "dec/alpha.c",
+ "dec/vp8l.c",
+ "dec/io.c",
+ "dec/vp8.c",
+ "dec/idec.c",
+ "dec/tree.c",
+ "dec/buffer.c",
+ "dec/quant.c",
+ "demux/demux.c",
+ "demux/anim_decode.c",
+ "dsp/yuv.c",
+ "dsp/filters_sse2.c",
+ "dsp/dec_sse41.c",
+ "dsp/rescaler.c",
+ "dsp/lossless_sse2.c",
+ "dsp/alpha_processing_sse41.c",
+ "dsp/alpha_processing_sse2.c",
+ "dsp/filters.c",
+ "dsp/upsampling_mips_dsp_r2.c",
+ "dsp/dec_neon.c",
+ "dsp/enc_neon.c",
+ "dsp/lossless_enc_mips32.c",
+ "dsp/lossless_enc_sse2.c",
+ "dsp/upsampling.c",
+ "dsp/lossless_enc_neon.c",
+ "dsp/alpha_processing.c",
+ "dsp/cost_sse2.c",
+ "dsp/dec_mips32.c",
+ "dsp/enc_avx2.c",
+ "dsp/rescaler_mips32.c",
+ "dsp/enc.c",
+ "dsp/lossless_enc_sse41.c",
+ "dsp/cost_mips32.c",
+ "dsp/lossless_mips_dsp_r2.c",
+ "dsp/filters_mips_dsp_r2.c",
+ "dsp/upsampling_neon.c",
+ "dsp/alpha_processing_mips_dsp_r2.c",
+ "dsp/enc_mips_dsp_r2.c",
+ "dsp/lossless.c",
+ "dsp/yuv_mips_dsp_r2.c",
+ "dsp/cost_mips_dsp_r2.c",
+ "dsp/argb.c",
+ "dsp/dec_sse2.c",
+ "dsp/rescaler_sse2.c",
+ "dsp/enc_sse41.c",
+ "dsp/argb_mips_dsp_r2.c",
+ "dsp/lossless_enc_mips_dsp_r2.c",
+ "dsp/dec_clip_tables.c",
+ "dsp/yuv_mips32.c",
+ "dsp/cpu.c",
+ "dsp/dec.c",
+ "dsp/argb_sse2.c",
+ "dsp/lossless_neon.c",
+ "dsp/lossless_enc.c",
+ "dsp/enc_mips32.c",
+ "dsp/cost.c",
+ "dsp/rescaler_mips_dsp_r2.c",
+ "dsp/dec_mips_dsp_r2.c",
+ "dsp/rescaler_neon.c",
+ "dsp/yuv_sse2.c",
+ "dsp/enc_sse2.c",
+ "dsp/upsampling_sse2.c",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_webp.add_source_files(env.modules_sources, thirdparty_sources)
+ env_webp.Append(CPPPATH = [thirdparty_dir])
+
+# Godot source files
+env_webp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webp/config.py b/modules/webp/config.py
new file mode 100644
index 0000000000..368e97e152
--- /dev/null
+++ b/modules/webp/config.py
@@ -0,0 +1,6 @@
+
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
new file mode 100644
index 0000000000..0fe2db3261
--- /dev/null
+++ b/modules/webp/image_loader_webp.cpp
@@ -0,0 +1,183 @@
+/*************************************************************************/
+/* image_loader_webp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "image_loader_webp.h"
+
+#include "io/marshalls.h"
+#include "print_string.h"
+#include "os/os.h"
+
+#include <stdlib.h>
+#include <webp/decode.h>
+#include <webp/encode.h>
+
+static DVector<uint8_t> _webp_lossy_pack(const Image& p_image,float p_quality) {
+
+ ERR_FAIL_COND_V(p_image.empty(),DVector<uint8_t>());
+
+ Image img=p_image;
+ if (img.detect_alpha())
+ img.convert(Image::FORMAT_RGBA);
+ else
+ img.convert(Image::FORMAT_RGB);
+
+ Size2 s(img.get_width(),img.get_height());
+ DVector<uint8_t> data = img.get_data();
+ DVector<uint8_t>::Read r = data.read();
+
+ uint8_t *dst_buff=NULL;
+ size_t dst_size=0;
+ if (img.get_format()==Image::FORMAT_RGB) {
+
+ dst_size = WebPEncodeRGB(r.ptr(),s.width,s.height,3*s.width,CLAMP(p_quality*100.0,0,100.0),&dst_buff);
+ } else {
+ dst_size = WebPEncodeRGBA(r.ptr(),s.width,s.height,4*s.width,CLAMP(p_quality*100.0,0,100.0),&dst_buff);
+ }
+
+ ERR_FAIL_COND_V(dst_size==0,DVector<uint8_t>());
+ DVector<uint8_t> dst;
+ dst.resize(4+dst_size);
+ DVector<uint8_t>::Write w = dst.write();
+ w[0]='W';
+ w[1]='E';
+ w[2]='B';
+ w[3]='P';
+ copymem(&w[4],dst_buff,dst_size);
+ free(dst_buff);
+ w=DVector<uint8_t>::Write();
+ return dst;
+}
+
+static Image _webp_lossy_unpack(const DVector<uint8_t>& p_buffer) {
+
+ int size = p_buffer.size()-4;
+ ERR_FAIL_COND_V(size<=0,Image());
+ DVector<uint8_t>::Read r = p_buffer.read();
+
+ ERR_FAIL_COND_V(r[0]!='W' || r[1]!='E' || r[2]!='B' || r[3]!='P',Image());
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(&r[4],size,&features)!=VP8_STATUS_OK) {
+ ERR_EXPLAIN("Error unpacking WEBP image:");
+ ERR_FAIL_V(Image());
+ }
+
+ //print_line("width: "+itos(features.width));
+ //print_line("height: "+itos(features.height));
+ //print_line("alpha: "+itos(features.has_alpha));
+
+ DVector<uint8_t> dst_image;
+ int datasize = features.width*features.height*(features.has_alpha?4:3);
+ dst_image.resize(datasize);
+
+ DVector<uint8_t>::Write dst_w = dst_image.write();
+
+ bool errdec=false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(&r[4],size,dst_w.ptr(),datasize,4*features.width)==NULL;
+ } else {
+ errdec = WebPDecodeRGBInto(&r[4],size,dst_w.ptr(),datasize,3*features.width)==NULL;
+
+ }
+
+ //ERR_EXPLAIN("Error decoding webp! - "+p_file);
+ ERR_FAIL_COND_V(errdec,Image());
+
+ dst_w = DVector<uint8_t>::Write();
+
+ return Image(features.width,features.height,0,features.has_alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB,dst_image);
+
+}
+
+
+Error ImageLoaderWEBP::load_image(Image *p_image,FileAccess *f) {
+
+
+ uint32_t size = f->get_len();
+ DVector<uint8_t> src_image;
+ src_image.resize(size);
+
+ WebPBitstreamFeatures features;
+
+ DVector<uint8_t>::Write src_w = src_image.write();
+ f->get_buffer(src_w.ptr(),size);
+ ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_EOF);
+
+ if (WebPGetFeatures(src_w.ptr(),size,&features)!=VP8_STATUS_OK) {
+ f->close();
+ //ERR_EXPLAIN("Error decoding WEBP image: "+p_file);
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+
+ print_line("width: "+itos(features.width));
+ print_line("height: "+itos(features.height));
+ print_line("alpha: "+itos(features.has_alpha));
+
+ src_w = DVector<uint8_t>::Write();
+
+ DVector<uint8_t> dst_image;
+ int datasize = features.width*features.height*(features.has_alpha?4:3);
+ dst_image.resize(datasize);
+
+ DVector<uint8_t>::Read src_r = src_image.read();
+ DVector<uint8_t>::Write dst_w = dst_image.write();
+
+
+ bool errdec=false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(src_r.ptr(),size,dst_w.ptr(),datasize,4*features.width)==NULL;
+ } else {
+ errdec = WebPDecodeRGBInto(src_r.ptr(),size,dst_w.ptr(),datasize,3*features.width)==NULL;
+
+ }
+
+ //ERR_EXPLAIN("Error decoding webp! - "+p_file);
+ ERR_FAIL_COND_V(errdec,ERR_FILE_CORRUPT);
+
+ src_r = DVector<uint8_t>::Read();
+ dst_w = DVector<uint8_t>::Write();
+
+ *p_image = Image(features.width,features.height,0,features.has_alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB,dst_image);
+
+
+ return OK;
+
+}
+
+void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
+
+ p_extensions->push_back("webp");
+}
+
+
+ImageLoaderWEBP::ImageLoaderWEBP() {
+
+ Image::lossy_packer=_webp_lossy_pack;
+ Image::lossy_unpacker=_webp_lossy_unpack;
+}
+
+
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
new file mode 100644
index 0000000000..24f79708db
--- /dev/null
+++ b/modules/webp/image_loader_webp.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* image_loader_webp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef IMAGE_LOADER_WEBP_H
+#define IMAGE_LOADER_WEBP_H
+
+#include "io/image_loader.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class ImageLoaderWEBP : public ImageFormatLoader {
+
+
+public:
+
+ virtual Error load_image(Image *p_image,FileAccess *f);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ ImageLoaderWEBP();
+};
+
+
+
+#endif
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
new file mode 100644
index 0000000000..039876bbb9
--- /dev/null
+++ b/modules/webp/register_types.cpp
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "image_loader_webp.h"
+
+static ImageLoaderWEBP *image_loader_webp = NULL;
+
+void register_webp_types() {
+
+ image_loader_webp = memnew( ImageLoaderWEBP );
+ ImageLoader::add_image_format_loader(image_loader_webp);
+}
+
+void unregister_webp_types() {
+
+ memdelete( image_loader_webp );
+}
diff --git a/modules/webp/register_types.h b/modules/webp/register_types.h
new file mode 100644
index 0000000000..a200188e47
--- /dev/null
+++ b/modules/webp/register_types.h
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_webp_types();
+void unregister_webp_types();